1use std::collections::{HashMap, VecDeque};
53use std::io::{self, Read, Write};
54use std::net::TcpStream;
55use std::sync::{Arc, Mutex, OnceLock};
56
57use crate::error::{Error, Result};
58use crate::tls::TlsStream;
59use crate::{Request, Response};
60
61const PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
67
68const F_DATA: u8 = 0x0;
70const F_HEADERS: u8 = 0x1;
71const F_PRIORITY: u8 = 0x2;
72const F_RST_STREAM: u8 = 0x3;
73const F_SETTINGS: u8 = 0x4;
74const F_PUSH_PROMISE: u8 = 0x5;
75const F_PING: u8 = 0x6;
76const F_GOAWAY: u8 = 0x7;
77const F_WINDOW_UPDATE: u8 = 0x8;
78const F_CONTINUATION: u8 = 0x9;
79
80const FLAG_END_STREAM: u8 = 0x01;
82const FLAG_ACK: u8 = 0x01;
83const FLAG_END_HEADERS: u8 = 0x04;
84const FLAG_PADDED: u8 = 0x08;
85const FLAG_PRIORITY: u8 = 0x20;
86
87const S_HEADER_TABLE_SIZE: u16 = 0x1;
89const S_ENABLE_PUSH: u16 = 0x2;
90const S_MAX_CONCURRENT_STREAMS: u16 = 0x3;
91const S_INITIAL_WINDOW_SIZE: u16 = 0x4;
92const S_MAX_FRAME_SIZE: u16 = 0x5;
93const S_MAX_HEADER_LIST_SIZE: u16 = 0x6;
94
95const INITIAL_WINDOW_SIZE_MAX: u32 = 0x7fff_ffff; const MAX_FRAME_SIZE_MIN: u32 = 16_384; const MAX_FRAME_SIZE_MAX: u32 = 16_777_215; const MAX_RESPONSE_BYTES: usize = 256 * 1024 * 1024;
104
105const MAX_HEADERS_BUF: usize = 256 * 1024;
111
112use crate::http::{header_octets_ok, MAX_DECODED_HEADER_LIST};
113
114const MAX_NO_PROGRESS_FRAMES: u32 = 10_000;
139
140const MAX_SETTINGS_FRAMES: u32 = 2_000;
144
145const MAX_PING_FRAMES: u32 = 2_000;
148
149const MAX_RST_STREAM_FRAMES: u32 = 2_000;
154
155#[derive(Debug, Clone, PartialEq, Eq)]
159struct PeerSettings {
160 header_table_size: u32,
161 enable_push: bool,
162 max_concurrent_streams: u32,
163 initial_window_size: u32,
164 max_frame_size: u32,
165 max_header_list_size: u32,
166}
167
168impl Default for PeerSettings {
169 fn default() -> Self {
170 PeerSettings {
173 header_table_size: 4096,
174 enable_push: true,
175 max_concurrent_streams: u32::MAX,
176 initial_window_size: 65_535,
177 max_frame_size: 16_384,
178 max_header_list_size: u32::MAX,
179 }
180 }
181}
182
183impl PeerSettings {
184 fn apply_settings_payload(&mut self, payload: &[u8]) -> Result<()> {
192 if !payload.len().is_multiple_of(6) {
193 return Err(Error::BadResponse(format!(
194 "SETTINGS payload length {} not a multiple of 6",
195 payload.len()
196 )));
197 }
198 for chunk in payload.chunks_exact(6) {
199 let id = u16::from_be_bytes([chunk[0], chunk[1]]);
200 let val = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
201 match id {
202 S_HEADER_TABLE_SIZE => self.header_table_size = val,
203 S_ENABLE_PUSH => {
204 self.enable_push = match val {
205 0 => false,
206 1 => true,
207 _ => {
208 return Err(Error::BadResponse(format!(
209 "SETTINGS_ENABLE_PUSH must be 0 or 1, got {val}"
210 )));
211 }
212 };
213 }
214 S_MAX_CONCURRENT_STREAMS => self.max_concurrent_streams = val,
215 S_INITIAL_WINDOW_SIZE => {
216 if val > INITIAL_WINDOW_SIZE_MAX {
217 return Err(Error::BadResponse(format!(
218 "SETTINGS_INITIAL_WINDOW_SIZE {val} exceeds 2^31-1 (FLOW_CONTROL_ERROR)"
219 )));
220 }
221 self.initial_window_size = val;
222 }
223 S_MAX_FRAME_SIZE => {
224 if !(MAX_FRAME_SIZE_MIN..=MAX_FRAME_SIZE_MAX).contains(&val) {
225 return Err(Error::BadResponse(format!(
226 "SETTINGS_MAX_FRAME_SIZE {val} out of range [16384, 16777215]"
227 )));
228 }
229 self.max_frame_size = val;
230 }
231 S_MAX_HEADER_LIST_SIZE => self.max_header_list_size = val,
232 _ => {
233 }
235 }
236 }
237 Ok(())
238 }
239}
240
241const WINDOW_MAX: i64 = 0x7fff_ffff;
259
260const OUR_INITIAL_WINDOW: i64 = 65_535;
265
266pub(crate) const DEFAULT_RECV_WINDOW: u32 = 8 * 1024 * 1024;
273
274#[derive(Debug, Clone, PartialEq, Eq)]
279struct ConnSendWindow {
280 available: i64,
281}
282
283impl ConnSendWindow {
284 fn new() -> Self {
285 ConnSendWindow { available: 65_535 }
286 }
287
288 fn apply_window_update(&mut self, increment: u32) -> Result<()> {
291 if increment == 0 {
292 return Err(Error::BadResponse(
293 "WINDOW_UPDATE with zero increment on connection (FLOW_CONTROL_ERROR)".into(),
294 ));
295 }
296 let new_val = self.available + increment as i64;
297 if new_val > WINDOW_MAX {
298 return Err(Error::BadResponse(format!(
299 "WINDOW_UPDATE pushes conn send window to {new_val} > 2^31-1 (FLOW_CONTROL_ERROR)"
300 )));
301 }
302 self.available = new_val;
303 Ok(())
304 }
305
306 fn consume(&mut self, n: usize) {
308 self.available -= n as i64;
309 }
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
316struct StreamSendWindow {
317 available: i64,
318 initial_peer_window: i64,
322}
323
324impl StreamSendWindow {
325 fn new(initial: i64) -> Self {
326 StreamSendWindow {
327 available: initial,
328 initial_peer_window: initial,
329 }
330 }
331
332 fn apply_window_update(&mut self, increment: u32) -> Result<()> {
335 if increment == 0 {
336 return Err(Error::BadResponse(
337 "WINDOW_UPDATE with zero increment on stream (PROTOCOL_ERROR)".into(),
338 ));
339 }
340 let new_val = self.available + increment as i64;
341 if new_val > WINDOW_MAX {
342 return Err(Error::BadResponse(format!(
343 "WINDOW_UPDATE pushes stream send window to {new_val} > 2^31-1 (FLOW_CONTROL_ERROR)"
344 )));
345 }
346 self.available = new_val;
347 Ok(())
348 }
349
350 fn apply_initial_window_change(&mut self, new_initial: u32) -> Result<()> {
354 let new_i = new_initial as i64;
355 let delta = new_i - self.initial_peer_window;
356 let new_available = self.available + delta;
357 if new_available > WINDOW_MAX {
358 return Err(Error::BadResponse(format!(
359 "SETTINGS_INITIAL_WINDOW_SIZE delta pushes stream send window to {new_available} > 2^31-1 (FLOW_CONTROL_ERROR)"
360 )));
361 }
362 self.available = new_available;
363 self.initial_peer_window = new_i;
364 Ok(())
365 }
366
367 fn consume(&mut self, n: usize) {
368 self.available -= n as i64;
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
376struct ConnRecvWindow {
377 available: i64,
378 initial: i64,
379}
380
381impl ConnRecvWindow {
382 fn new(initial: i64) -> Self {
386 ConnRecvWindow {
387 available: initial,
388 initial,
389 }
390 }
391
392 fn consume(&mut self, n: usize) {
393 self.available -= n as i64;
394 }
395
396 fn replenish(&mut self) -> Option<Frame> {
399 let threshold = self.initial / 2;
400 if self.available < threshold {
401 let inc = (self.initial - self.available) as u32;
402 self.available = self.initial;
403 Some(window_update_frame(0, inc))
404 } else {
405 None
406 }
407 }
408}
409
410#[derive(Debug, Clone, PartialEq, Eq)]
413struct StreamRecvWindow {
414 available: i64,
415 initial: i64,
416}
417
418impl StreamRecvWindow {
419 fn new(initial: i64) -> Self {
422 StreamRecvWindow {
423 available: initial,
424 initial,
425 }
426 }
427
428 fn consume(&mut self, n: usize) {
429 self.available -= n as i64;
430 }
431
432 fn replenish(&mut self, stream_id: u32) -> Option<Frame> {
433 let threshold = self.initial / 2;
434 if self.available < threshold {
435 let inc = (self.initial - self.available) as u32;
436 self.available = self.initial;
437 Some(window_update_frame(stream_id, inc))
438 } else {
439 None
440 }
441 }
442}
443
444fn window_update_frame(stream_id: u32, increment: u32) -> Frame {
447 let mut payload = Vec::with_capacity(4);
448 payload.extend_from_slice(&(increment & 0x7fff_ffff).to_be_bytes());
449 Frame {
450 typ: F_WINDOW_UPDATE,
451 flags: 0,
452 stream_id,
453 payload,
454 }
455}
456
457fn parse_window_update(payload: &[u8]) -> Result<u32> {
461 if payload.len() != 4 {
462 return Err(Error::BadResponse(format!(
463 "WINDOW_UPDATE payload length {} (expected 4) (FRAME_SIZE_ERROR)",
464 payload.len()
465 )));
466 }
467 let raw = u32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
468 Ok(raw & 0x7fff_ffff)
469}
470
471#[derive(Debug, Clone, PartialEq, Eq)]
473struct Frame {
474 typ: u8,
475 flags: u8,
476 stream_id: u32,
477 payload: Vec<u8>,
478}
479
480const MAX_FRAME_PAYLOAD: usize = 1 << 20; fn read_exact<R: Read>(r: &mut R, buf: &mut [u8]) -> io::Result<()> {
483 r.read_exact(buf)
484}
485
486fn read_frame<R: Read>(r: &mut R) -> io::Result<Frame> {
487 let mut hdr = [0u8; 9];
488 read_exact(r, &mut hdr)?;
489 let length = ((hdr[0] as usize) << 16) | ((hdr[1] as usize) << 8) | (hdr[2] as usize);
490 let typ = hdr[3];
491 let flags = hdr[4];
492 let stream_id = (((hdr[5] & 0x7f) as u32) << 24)
493 | ((hdr[6] as u32) << 16)
494 | ((hdr[7] as u32) << 8)
495 | (hdr[8] as u32);
496 if length > MAX_FRAME_PAYLOAD {
497 return Err(io::Error::new(
498 io::ErrorKind::InvalidData,
499 format!("frame payload too large: {length}"),
500 ));
501 }
502 let mut payload = vec![0u8; length];
503 if length > 0 {
504 read_exact(r, &mut payload)?;
505 }
506 Ok(Frame {
507 typ,
508 flags,
509 stream_id,
510 payload,
511 })
512}
513
514fn write_frame<W: Write>(w: &mut W, f: &Frame) -> io::Result<()> {
515 if f.payload.len() > MAX_FRAME_PAYLOAD {
516 return Err(io::Error::new(
517 io::ErrorKind::InvalidInput,
518 "frame payload too large",
519 ));
520 }
521 let len = f.payload.len();
522 let hdr = [
523 ((len >> 16) & 0xff) as u8,
524 ((len >> 8) & 0xff) as u8,
525 (len & 0xff) as u8,
526 f.typ,
527 f.flags,
528 ((f.stream_id >> 24) & 0x7f) as u8, ((f.stream_id >> 16) & 0xff) as u8,
530 ((f.stream_id >> 8) & 0xff) as u8,
531 (f.stream_id & 0xff) as u8,
532 ];
533 w.write_all(&hdr)?;
534 if !f.payload.is_empty() {
535 w.write_all(&f.payload)?;
536 }
537 Ok(())
538}
539
540fn priority_weight_byte(priority: crate::http::Priority) -> Option<u8> {
544 match priority {
545 crate::http::Priority::High => Some(255), crate::http::Priority::Normal => None, crate::http::Priority::Low => Some(0), }
549}
550
551fn encode_int(value: u64, prefix_bits: u8) -> Vec<u8> {
559 let max_prefix: u64 = (1u64 << prefix_bits) - 1;
560 let mut out = Vec::new();
561 if value < max_prefix {
562 out.push(value as u8);
563 return out;
564 }
565 out.push(max_prefix as u8);
566 let mut rem = value - max_prefix;
567 while rem >= 128 {
568 out.push(((rem & 0x7f) as u8) | 0x80);
569 rem >>= 7;
570 }
571 out.push(rem as u8);
572 out
573}
574
575fn decode_int(buf: &[u8], prefix_bits: u8) -> Result<(u64, usize)> {
577 if buf.is_empty() {
578 return Err(Error::BadResponse("hpack: empty integer".into()));
579 }
580 let max_prefix: u64 = (1u64 << prefix_bits) - 1;
581 let mut value = (buf[0] as u64) & max_prefix;
582 if value < max_prefix {
583 return Ok((value, 1));
584 }
585 let mut i = 1usize;
586 let mut shift = 0u32;
587 loop {
588 if i >= buf.len() {
589 return Err(Error::BadResponse("hpack: truncated integer".into()));
590 }
591 let b = buf[i];
592 i += 1;
593 value = value
594 .checked_add(((b & 0x7f) as u64) << shift)
595 .ok_or_else(|| Error::BadResponse("hpack: integer overflow".into()))?;
596 if b & 0x80 == 0 {
597 return Ok((value, i));
598 }
599 shift += 7;
600 if shift > 63 {
601 return Err(Error::BadResponse("hpack: integer overflow".into()));
602 }
603 }
604}
605
606const STATIC_TABLE: &[(&str, &str)] = &[
612 (":authority", ""), (":method", "GET"), (":method", "POST"), (":path", "/"), (":path", "/index.html"), (":scheme", "http"), (":scheme", "https"), (":status", "200"), (":status", "204"), (":status", "206"), (":status", "304"), (":status", "400"), (":status", "404"), (":status", "500"), ("accept-charset", ""), ("accept-encoding", "gzip, deflate"), ("accept-language", ""), ("accept-ranges", ""), ("accept", ""), ("access-control-allow-origin", ""), ("age", ""), ("allow", ""), ("authorization", ""), ("cache-control", ""), ("content-disposition", ""), ("content-encoding", ""), ("content-language", ""), ("content-length", ""), ("content-location", ""), ("content-range", ""), ("content-type", ""), ("cookie", ""), ("date", ""), ("etag", ""), ("expect", ""), ("expires", ""), ("from", ""), ("host", ""), ("if-match", ""), ("if-modified-since", ""), ("if-none-match", ""), ("if-range", ""), ("if-unmodified-since", ""), ("last-modified", ""), ("link", ""), ("location", ""), ("max-forwards", ""), ("proxy-authenticate", ""), ("proxy-authorization", ""), ("range", ""), ("referer", ""), ("refresh", ""), ("retry-after", ""), ("server", ""), ("set-cookie", ""), ("strict-transport-security", ""), ("transfer-encoding", ""), ("user-agent", ""), ("vary", ""), ("via", ""), ("www-authenticate", ""), ];
674
675fn static_full_index(name: &str, value: &str) -> Option<usize> {
677 STATIC_TABLE
678 .iter()
679 .position(|(n, v)| *n == name && *v == value)
680 .map(|i| i + 1)
681}
682
683fn static_name_index(name: &str) -> Option<usize> {
685 STATIC_TABLE
686 .iter()
687 .position(|(n, _)| *n == name)
688 .map(|i| i + 1)
689}
690
691const HUFFMAN: [(u32, u8); 257] = [
704 (0x1ff8, 13),
705 (0x7fffd8, 23),
706 (0xfffffe2, 28),
707 (0xfffffe3, 28),
708 (0xfffffe4, 28),
709 (0xfffffe5, 28),
710 (0xfffffe6, 28),
711 (0xfffffe7, 28),
712 (0xfffffe8, 28),
713 (0xffffea, 24),
714 (0x3ffffffc, 30),
715 (0xfffffe9, 28),
716 (0xfffffea, 28),
717 (0x3ffffffd, 30),
718 (0xfffffeb, 28),
719 (0xfffffec, 28),
720 (0xfffffed, 28),
721 (0xfffffee, 28),
722 (0xfffffef, 28),
723 (0xffffff0, 28),
724 (0xffffff1, 28),
725 (0xffffff2, 28),
726 (0x3ffffffe, 30),
727 (0xffffff3, 28),
728 (0xffffff4, 28),
729 (0xffffff5, 28),
730 (0xffffff6, 28),
731 (0xffffff7, 28),
732 (0xffffff8, 28),
733 (0xffffff9, 28),
734 (0xffffffa, 28),
735 (0xffffffb, 28),
736 (0x14, 6),
737 (0x3f8, 10),
738 (0x3f9, 10),
739 (0xffa, 12),
740 (0x1ff9, 13),
741 (0x15, 6),
742 (0xf8, 8),
743 (0x7fa, 11),
744 (0x3fa, 10),
745 (0x3fb, 10),
746 (0xf9, 8),
747 (0x7fb, 11),
748 (0xfa, 8),
749 (0x16, 6),
750 (0x17, 6),
751 (0x18, 6),
752 (0x0, 5),
753 (0x1, 5),
754 (0x2, 5),
755 (0x19, 6),
756 (0x1a, 6),
757 (0x1b, 6),
758 (0x1c, 6),
759 (0x1d, 6),
760 (0x1e, 6),
761 (0x1f, 6),
762 (0x5c, 7),
763 (0xfb, 8),
764 (0x7ffc, 15),
765 (0x20, 6),
766 (0xffb, 12),
767 (0x3fc, 10),
768 (0x1ffa, 13),
769 (0x21, 6),
770 (0x5d, 7),
771 (0x5e, 7),
772 (0x5f, 7),
773 (0x60, 7),
774 (0x61, 7),
775 (0x62, 7),
776 (0x63, 7),
777 (0x64, 7),
778 (0x65, 7),
779 (0x66, 7),
780 (0x67, 7),
781 (0x68, 7),
782 (0x69, 7),
783 (0x6a, 7),
784 (0x6b, 7),
785 (0x6c, 7),
786 (0x6d, 7),
787 (0x6e, 7),
788 (0x6f, 7),
789 (0x70, 7),
790 (0x71, 7),
791 (0x72, 7),
792 (0xfc, 8),
793 (0x73, 7),
794 (0xfd, 8),
795 (0x1ffb, 13),
796 (0x7fff0, 19),
797 (0x1ffc, 13),
798 (0x3ffc, 14),
799 (0x22, 6),
800 (0x7ffd, 15),
801 (0x3, 5),
802 (0x23, 6),
803 (0x4, 5),
804 (0x24, 6),
805 (0x5, 5),
806 (0x25, 6),
807 (0x26, 6),
808 (0x27, 6),
809 (0x6, 5),
810 (0x74, 7),
811 (0x75, 7),
812 (0x28, 6),
813 (0x29, 6),
814 (0x2a, 6),
815 (0x7, 5),
816 (0x2b, 6),
817 (0x76, 7),
818 (0x2c, 6),
819 (0x8, 5),
820 (0x9, 5),
821 (0x2d, 6),
822 (0x77, 7),
823 (0x78, 7),
824 (0x79, 7),
825 (0x7a, 7),
826 (0x7b, 7),
827 (0x7ffe, 15),
828 (0x7fc, 11),
829 (0x3ffd, 14),
830 (0x1ffd, 13),
831 (0xffffffc, 28),
832 (0xfffe6, 20),
833 (0x3fffd2, 22),
834 (0xfffe7, 20),
835 (0xfffe8, 20),
836 (0x3fffd3, 22),
837 (0x3fffd4, 22),
838 (0x3fffd5, 22),
839 (0x7fffd9, 23),
840 (0x3fffd6, 22),
841 (0x7fffda, 23),
842 (0x7fffdb, 23),
843 (0x7fffdc, 23),
844 (0x7fffdd, 23),
845 (0x7fffde, 23),
846 (0xffffeb, 24),
847 (0x7fffdf, 23),
848 (0xffffec, 24),
849 (0xffffed, 24),
850 (0x3fffd7, 22),
851 (0x7fffe0, 23),
852 (0xffffee, 24),
853 (0x7fffe1, 23),
854 (0x7fffe2, 23),
855 (0x7fffe3, 23),
856 (0x7fffe4, 23),
857 (0x1fffdc, 21),
858 (0x3fffd8, 22),
859 (0x7fffe5, 23),
860 (0x3fffd9, 22),
861 (0x7fffe6, 23),
862 (0x7fffe7, 23),
863 (0xffffef, 24),
864 (0x3fffda, 22),
865 (0x1fffdd, 21),
866 (0xfffe9, 20),
867 (0x3fffdb, 22),
868 (0x3fffdc, 22),
869 (0x7fffe8, 23),
870 (0x7fffe9, 23),
871 (0x1fffde, 21),
872 (0x7fffea, 23),
873 (0x3fffdd, 22),
874 (0x3fffde, 22),
875 (0xfffff0, 24),
876 (0x1fffdf, 21),
877 (0x3fffdf, 22),
878 (0x7fffeb, 23),
879 (0x7fffec, 23),
880 (0x1fffe0, 21),
881 (0x1fffe1, 21),
882 (0x3fffe0, 22),
883 (0x1fffe2, 21),
884 (0x7fffed, 23),
885 (0x3fffe1, 22),
886 (0x7fffee, 23),
887 (0x7fffef, 23),
888 (0xfffea, 20),
889 (0x3fffe2, 22),
890 (0x3fffe3, 22),
891 (0x3fffe4, 22),
892 (0x7ffff0, 23),
893 (0x3fffe5, 22),
894 (0x3fffe6, 22),
895 (0x7ffff1, 23),
896 (0x3ffffe0, 26),
897 (0x3ffffe1, 26),
898 (0xfffeb, 20),
899 (0x7fff1, 19),
900 (0x3fffe7, 22),
901 (0x7ffff2, 23),
902 (0x3fffe8, 22),
903 (0x1ffffec, 25),
904 (0x3ffffe2, 26),
905 (0x3ffffe3, 26),
906 (0x3ffffe4, 26),
907 (0x7ffffde, 27),
908 (0x7ffffdf, 27),
909 (0x3ffffe5, 26),
910 (0xfffff1, 24),
911 (0x1ffffed, 25),
912 (0x7fff2, 19),
913 (0x1fffe3, 21),
914 (0x3ffffe6, 26),
915 (0x7ffffe0, 27),
916 (0x7ffffe1, 27),
917 (0x3ffffe7, 26),
918 (0x7ffffe2, 27),
919 (0xfffff2, 24),
920 (0x1fffe4, 21),
921 (0x1fffe5, 21),
922 (0x3ffffe8, 26),
923 (0x3ffffe9, 26),
924 (0xffffffd, 28),
925 (0x7ffffe3, 27),
926 (0x7ffffe4, 27),
927 (0x7ffffe5, 27),
928 (0xfffec, 20),
929 (0xfffff3, 24),
930 (0xfffed, 20),
931 (0x1fffe6, 21),
932 (0x3fffe9, 22),
933 (0x1fffe7, 21),
934 (0x1fffe8, 21),
935 (0x7ffff3, 23),
936 (0x3fffea, 22),
937 (0x3fffeb, 22),
938 (0x1ffffee, 25),
939 (0x1ffffef, 25),
940 (0xfffff4, 24),
941 (0xfffff5, 24),
942 (0x3ffffea, 26),
943 (0x7ffff4, 23),
944 (0x3ffffeb, 26),
945 (0x7ffffe6, 27),
946 (0x3ffffec, 26),
947 (0x3ffffed, 26),
948 (0x7ffffe7, 27),
949 (0x7ffffe8, 27),
950 (0x7ffffe9, 27),
951 (0x7ffffea, 27),
952 (0x7ffffeb, 27),
953 (0xffffffe, 28),
954 (0x7ffffec, 27),
955 (0x7ffffed, 27),
956 (0x7ffffee, 27),
957 (0x7ffffef, 27),
958 (0x7fffff0, 27),
959 (0x3ffffee, 26),
960 (0x3fffffff, 30), ];
962
963fn huffman_decode(input: &[u8]) -> Result<Vec<u8>> {
968 let mut out = Vec::with_capacity(input.len().saturating_mul(2));
969 let mut acc: u64 = 0;
970 let mut acc_len: u8 = 0;
971
972 for &byte in input {
973 acc = (acc << 8) | (byte as u64);
974 acc_len += 8;
975 while acc_len >= 5 {
977 let mut matched = false;
978 let max_len = acc_len.min(30);
980 for try_len in 5..=max_len {
981 let code = (acc >> (acc_len - try_len)) & ((1u64 << try_len) - 1);
982 if let Some(sym) = lookup_huffman(code as u32, try_len) {
984 if sym == 256 {
985 return Err(Error::BadResponse(
987 "hpack: EOS symbol in Huffman literal".into(),
988 ));
989 }
990 out.push(sym as u8);
991 acc_len -= try_len;
992 matched = true;
993 break;
994 }
995 }
996 if !matched {
997 break;
998 }
999 }
1000 }
1001
1002 if acc_len >= 8 {
1005 return Err(Error::BadResponse(
1006 "hpack: trailing Huffman bits >= 8".into(),
1007 ));
1008 }
1009 if acc_len > 0 {
1010 let pad_mask = (1u64 << acc_len) - 1;
1011 let tail = acc & pad_mask;
1012 if tail != pad_mask {
1013 return Err(Error::BadResponse("hpack: bad Huffman padding".into()));
1014 }
1015 }
1016 Ok(out)
1017}
1018
1019fn lookup_huffman(code: u32, len: u8) -> Option<u16> {
1020 for (i, (c, l)) in HUFFMAN.iter().enumerate() {
1021 if *l == len && *c == code {
1022 return Some(i as u16);
1023 }
1024 }
1025 None
1026}
1027
1028const DYN_TABLE_CAP: usize = 4096;
1035
1036struct Decoder {
1039 dyn_table: Vec<(String, String)>,
1040 dyn_table_size: usize,
1041 dyn_table_cap: usize,
1042}
1043
1044impl Decoder {
1045 fn new() -> Self {
1046 Decoder {
1047 dyn_table: Vec::new(),
1048 dyn_table_size: 0,
1049 dyn_table_cap: DYN_TABLE_CAP,
1050 }
1051 }
1052
1053 fn entry_size(name: &str, value: &str) -> usize {
1054 name.len() + value.len() + 32
1055 }
1056
1057 fn evict_to_fit(&mut self, incoming: usize) {
1058 while self.dyn_table_size + incoming > self.dyn_table_cap && !self.dyn_table.is_empty() {
1059 let (n, v) = self.dyn_table.pop().unwrap();
1060 self.dyn_table_size = self.dyn_table_size.saturating_sub(Self::entry_size(&n, &v));
1061 }
1062 }
1063
1064 fn insert(&mut self, name: String, value: String) {
1065 let sz = Self::entry_size(&name, &value);
1066 if sz > self.dyn_table_cap {
1067 self.dyn_table.clear();
1069 self.dyn_table_size = 0;
1070 return;
1071 }
1072 self.evict_to_fit(sz);
1073 self.dyn_table.insert(0, (name, value));
1074 self.dyn_table_size += sz;
1075 }
1076
1077 fn lookup(&self, index: u64) -> Result<(String, String)> {
1078 if index == 0 {
1079 return Err(Error::BadResponse("hpack: index 0".into()));
1080 }
1081 let idx = index as usize;
1082 if idx <= STATIC_TABLE.len() {
1083 let (n, v) = STATIC_TABLE[idx - 1];
1084 return Ok((n.to_string(), v.to_string()));
1085 }
1086 let dyn_idx = idx - STATIC_TABLE.len() - 1;
1087 if dyn_idx >= self.dyn_table.len() {
1088 return Err(Error::BadResponse(format!(
1089 "hpack: index {idx} out of range"
1090 )));
1091 }
1092 let (n, v) = &self.dyn_table[dyn_idx];
1093 Ok((n.clone(), v.clone()))
1094 }
1095
1096 fn lookup_name(&self, index: u64) -> Result<String> {
1097 Ok(self.lookup(index)?.0)
1098 }
1099
1100 fn read_string(&self, buf: &[u8], pos: &mut usize) -> Result<String> {
1103 if *pos >= buf.len() {
1104 return Err(Error::BadResponse("hpack: truncated string".into()));
1105 }
1106 let huffman = buf[*pos] & 0x80 != 0;
1107 let (len, consumed) = decode_int(&buf[*pos..], 7)?;
1108 *pos += consumed;
1109 let end = pos
1110 .checked_add(len as usize)
1111 .ok_or_else(|| Error::BadResponse("hpack: string length overflow".into()))?;
1112 if end > buf.len() {
1113 return Err(Error::BadResponse("hpack: truncated string body".into()));
1114 }
1115 let raw = &buf[*pos..end];
1116 *pos = end;
1117 if huffman {
1118 let bytes = huffman_decode(raw)?;
1119 String::from_utf8(bytes)
1120 .map_err(|_| Error::BadResponse("hpack: non-utf8 Huffman literal".into()))
1121 } else {
1122 String::from_utf8(raw.to_vec())
1123 .map_err(|_| Error::BadResponse("hpack: non-utf8 literal".into()))
1124 }
1125 }
1126
1127 fn decode_block(&mut self, buf: &[u8]) -> Result<Vec<(String, String)>> {
1128 let mut out = Vec::new();
1129 let mut pos = 0;
1130 let mut list_size: usize = 0;
1134 while pos < buf.len() {
1135 let b = buf[pos];
1136 let entry: (String, String);
1137 if b & 0x80 != 0 {
1138 let (idx, n) = decode_int(&buf[pos..], 7)?;
1140 pos += n;
1141 entry = self.lookup(idx)?;
1142 } else if b & 0x40 != 0 {
1143 let (idx, n) = decode_int(&buf[pos..], 6)?;
1145 pos += n;
1146 let name = if idx == 0 {
1147 self.read_string(buf, &mut pos)?
1148 } else {
1149 self.lookup_name(idx)?
1150 };
1151 let value = self.read_string(buf, &mut pos)?;
1152 self.insert(name.clone(), value.clone());
1153 entry = (name, value);
1154 } else if b & 0x20 != 0 {
1155 let (new_size, n) = decode_int(&buf[pos..], 5)?;
1157 pos += n;
1158 let cap = (new_size as usize).min(DYN_TABLE_CAP);
1159 self.dyn_table_cap = cap;
1160 self.evict_to_fit(0);
1161 continue;
1162 } else {
1163 let (idx, n) = decode_int(&buf[pos..], 4)?;
1168 pos += n;
1169 let name = if idx == 0 {
1170 self.read_string(buf, &mut pos)?
1171 } else {
1172 self.lookup_name(idx)?
1173 };
1174 let value = self.read_string(buf, &mut pos)?;
1175 entry = (name, value);
1176 }
1177 if !header_octets_ok(entry.0.as_bytes(), entry.1.as_bytes()) {
1183 return Err(Error::BadResponse(
1184 "hpack: forbidden octet in decoded header".into(),
1185 ));
1186 }
1187 list_size = list_size
1188 .saturating_add(entry.0.len())
1189 .saturating_add(entry.1.len())
1190 .saturating_add(32);
1191 if list_size > MAX_DECODED_HEADER_LIST {
1192 return Err(Error::BadResponse(
1193 "hpack: decoded header list exceeds limit".into(),
1194 ));
1195 }
1196 out.push(entry);
1197 }
1198 Ok(out)
1199 }
1200}
1201
1202fn huffman_encode(input: &[u8]) -> Vec<u8> {
1206 let total_bits: usize = input.iter().map(|b| HUFFMAN[*b as usize].1 as usize).sum();
1208 let out_len = total_bits.div_ceil(8);
1209 let mut out = vec![0u8; out_len];
1210
1211 let mut bit_pos: usize = 0;
1215 for &b in input {
1216 let (code, len) = HUFFMAN[b as usize];
1217 let len = len as usize;
1218 let mut remaining = len;
1220 let mut code_left = code as u64;
1221 while remaining > 0 {
1222 let byte_index = bit_pos / 8;
1223 let bit_in_byte = bit_pos % 8; let space_in_byte = 8 - bit_in_byte;
1225 let take = remaining.min(space_in_byte);
1226 let shift = (remaining - take) as u32;
1228 let chunk = ((code_left >> shift) & ((1u64 << take) - 1)) as u8;
1229 out[byte_index] |= chunk << (space_in_byte - take);
1232 if shift > 0 {
1234 code_left &= (1u64 << shift) - 1;
1235 } else {
1236 code_left = 0;
1237 }
1238 remaining -= take;
1239 bit_pos += take;
1240 }
1241 }
1242
1243 let trailing = (8 - (total_bits % 8)) % 8;
1246 if trailing > 0 {
1247 let last = out.len() - 1;
1248 out[last] |= (1u8 << trailing) - 1;
1249 }
1250 out
1251}
1252
1253fn encode_literal_string(out: &mut Vec<u8>, s: &str) {
1258 let raw = s.as_bytes();
1259 let huff = huffman_encode(raw);
1260 if huff.len() < raw.len() {
1261 let mut len_bytes = encode_int(huff.len() as u64, 7);
1262 len_bytes[0] |= 0x80; out.extend_from_slice(&len_bytes);
1264 out.extend_from_slice(&huff);
1265 } else {
1266 let mut len_bytes = encode_int(raw.len() as u64, 7);
1267 len_bytes[0] &= 0x7f; out.extend_from_slice(&len_bytes);
1269 out.extend_from_slice(raw);
1270 }
1271}
1272
1273struct Encoder {
1295 dyn_table: VecDeque<(String, String)>,
1296 dyn_table_size: usize,
1297 max_dyn_table_size: usize,
1298 pending_max_table_size_signal: Option<usize>,
1303}
1304
1305impl Encoder {
1306 fn new() -> Self {
1307 Encoder {
1308 dyn_table: VecDeque::new(),
1309 dyn_table_size: 0,
1310 max_dyn_table_size: DYN_TABLE_CAP,
1311 pending_max_table_size_signal: None,
1312 }
1313 }
1314
1315 fn entry_size(name: &str, value: &str) -> usize {
1316 name.len() + value.len() + 32
1317 }
1318
1319 fn set_peer_max_table_size(&mut self, n: usize) {
1324 self.max_dyn_table_size = n;
1325 self.evict_to_fit(0);
1326 self.pending_max_table_size_signal = Some(n);
1327 }
1328
1329 fn evict_to_fit(&mut self, incoming: usize) {
1330 while self.dyn_table_size + incoming > self.max_dyn_table_size && !self.dyn_table.is_empty()
1331 {
1332 let (n, v) = self.dyn_table.pop_back().unwrap();
1334 self.dyn_table_size = self.dyn_table_size.saturating_sub(Self::entry_size(&n, &v));
1335 }
1336 }
1337
1338 fn insert(&mut self, name: &str, value: &str) {
1339 let sz = Self::entry_size(name, value);
1340 if sz > self.max_dyn_table_size {
1341 self.dyn_table.clear();
1343 self.dyn_table_size = 0;
1344 return;
1345 }
1346 self.evict_to_fit(sz);
1347 self.dyn_table
1348 .push_front((name.to_string(), value.to_string()));
1349 self.dyn_table_size += sz;
1350 }
1351
1352 fn combined_full_index(&self, name: &str, value: &str) -> Option<u32> {
1356 if let Some(i) = static_full_index(name, value) {
1357 return Some(i as u32);
1358 }
1359 for (i, (n, v)) in self.dyn_table.iter().enumerate() {
1360 if n == name && v == value {
1361 return Some((STATIC_TABLE.len() + 1 + i) as u32);
1362 }
1363 }
1364 None
1365 }
1366
1367 fn combined_name_index(&self, name: &str) -> Option<u32> {
1369 if let Some(i) = static_name_index(name) {
1370 return Some(i as u32);
1371 }
1372 for (i, (n, _)) in self.dyn_table.iter().enumerate() {
1373 if n == name {
1374 return Some((STATIC_TABLE.len() + 1 + i) as u32);
1375 }
1376 }
1377 None
1378 }
1379
1380 fn encode_header(&mut self, out: &mut Vec<u8>, name: &str, value: &str) {
1392 if let Some(n) = self.pending_max_table_size_signal.take() {
1394 let mut bytes = encode_int(n as u64, 5);
1395 bytes[0] |= 0x20;
1396 out.extend_from_slice(&bytes);
1397 }
1398
1399 if let Some(idx) = self.combined_full_index(name, value) {
1401 let mut bytes = encode_int(idx as u64, 7);
1402 bytes[0] |= 0x80;
1403 out.extend_from_slice(&bytes);
1404 return;
1405 }
1406
1407 if let Some(idx) = self.combined_name_index(name) {
1409 let mut bytes = encode_int(idx as u64, 6);
1410 bytes[0] |= 0x40;
1411 out.extend_from_slice(&bytes);
1412 encode_literal_string(out, value);
1413 self.insert(name, value);
1414 return;
1415 }
1416
1417 out.push(0x40);
1419 encode_literal_string(out, name);
1420 encode_literal_string(out, value);
1421 self.insert(name, value);
1422 }
1423}
1424
1425#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1439enum StreamState {
1440 Idle,
1441 Open,
1442 HalfClosedLocal,
1443 HalfClosedRemote,
1444 Closed,
1445}
1446
1447impl StreamState {
1448 fn send_data(self, end_stream: bool) -> Result<StreamState> {
1451 match self {
1452 StreamState::Idle => {
1453 if end_stream {
1454 Ok(StreamState::HalfClosedLocal)
1455 } else {
1456 Ok(StreamState::Open)
1457 }
1458 }
1459 StreamState::Open => Ok(if end_stream {
1460 StreamState::HalfClosedLocal
1461 } else {
1462 StreamState::Open
1463 }),
1464 StreamState::HalfClosedRemote => Ok(if end_stream {
1465 StreamState::Closed
1466 } else {
1467 StreamState::HalfClosedRemote
1468 }),
1469 StreamState::HalfClosedLocal | StreamState::Closed => Err(Error::BadResponse(format!(
1470 "internal: tried to send DATA in stream state {self:?}"
1471 ))),
1472 }
1473 }
1474
1475 fn recv_data(self, end_stream: bool) -> Result<StreamState> {
1477 match self {
1478 StreamState::Open => Ok(if end_stream {
1479 StreamState::HalfClosedRemote
1480 } else {
1481 StreamState::Open
1482 }),
1483 StreamState::HalfClosedLocal => Ok(if end_stream {
1484 StreamState::Closed
1485 } else {
1486 StreamState::HalfClosedLocal
1487 }),
1488 StreamState::Idle | StreamState::HalfClosedRemote | StreamState::Closed => {
1489 Err(Error::BadResponse(format!(
1490 "received DATA in stream state {self:?} (RFC 9113 §5.1)"
1491 )))
1492 }
1493 }
1494 }
1495
1496 fn recv_headers(self, end_stream: bool) -> Result<StreamState> {
1499 match self {
1500 StreamState::Open => Ok(if end_stream {
1501 StreamState::HalfClosedRemote
1502 } else {
1503 StreamState::Open
1504 }),
1505 StreamState::HalfClosedLocal => Ok(if end_stream {
1506 StreamState::Closed
1507 } else {
1508 StreamState::HalfClosedLocal
1509 }),
1510 StreamState::Closed => Ok(StreamState::Closed),
1511 StreamState::Idle | StreamState::HalfClosedRemote => Err(Error::BadResponse(format!(
1512 "received HEADERS in stream state {self:?} (RFC 9113 §5.1)"
1513 ))),
1514 }
1515 }
1516
1517 fn recv_rst(self) -> Result<StreamState> {
1520 match self {
1521 StreamState::Idle => Err(Error::BadResponse(
1522 "RST_STREAM on idle stream (RFC 9113 §5.1)".into(),
1523 )),
1524 _ => Ok(StreamState::Closed),
1525 }
1526 }
1527}
1528
1529struct Stream {
1533 #[allow(dead_code)]
1537 id: u32,
1538 state: StreamState,
1539 send_window: StreamSendWindow,
1540 recv_window: StreamRecvWindow,
1541 headers_buf: Vec<u8>,
1544 response_headers: Option<Vec<(String, String)>>,
1547 body: Vec<u8>,
1551 streamed_len: u64,
1554 end_stream_recv: bool,
1556 pending_body: Option<PendingBody>,
1564}
1565
1566struct PendingBody {
1569 bytes: Vec<u8>,
1571 sent: usize,
1573}
1574
1575impl Stream {
1576 fn new(id: u32, initial_peer_window: i64, our_recv_window: i64) -> Self {
1577 Stream {
1578 id,
1579 state: StreamState::Idle,
1580 send_window: StreamSendWindow::new(initial_peer_window),
1581 recv_window: StreamRecvWindow::new(our_recv_window),
1582 headers_buf: Vec::new(),
1583 response_headers: None,
1584 body: Vec::new(),
1585 streamed_len: 0,
1586 end_stream_recv: false,
1587 pending_body: None,
1588 }
1589 }
1590
1591 fn send_budget(&self, conn_window: &ConnSendWindow) -> i64 {
1594 self.send_window.available.min(conn_window.available)
1595 }
1596
1597 fn push_header_fragment(&mut self, frag: &[u8]) -> Result<()> {
1604 if self.headers_buf.len().saturating_add(frag.len()) > MAX_HEADERS_BUF {
1605 return Err(Error::BadResponse(
1606 "header block exceeds size limit (CONTINUATION flood?)".into(),
1607 ));
1608 }
1609 self.headers_buf.extend_from_slice(frag);
1610 Ok(())
1611 }
1612}
1613
1614struct Connection<S: Read + Write> {
1632 tls: S,
1633 peer: PeerSettings,
1634 conn_send_window: ConnSendWindow,
1635 conn_recv_window: ConnRecvWindow,
1636 our_recv_window: i64,
1639 decoder: Decoder,
1640 encoder: Encoder,
1641 streams: HashMap<u32, Stream>,
1642 next_stream_id: u32,
1645 goaway_received: Option<u32>,
1649 expecting_continuation: Option<u32>,
1654 budget: FloodBudget,
1659 made_progress: bool,
1664 tls_info: Option<crate::http::TlsInfo>,
1668 dial_timing: crate::http::Timing,
1672}
1673
1674#[derive(Debug, Default, Clone, PartialEq, Eq)]
1678struct FloodBudget {
1679 no_progress: u32,
1682 settings: u32,
1684 ping: u32,
1686 rst_stream: u32,
1688}
1689
1690impl FloodBudget {
1691 fn record_control_frame(&mut self, typ: u8, flags: u8) -> Result<()> {
1700 match typ {
1701 F_SETTINGS if flags & FLAG_ACK == 0 => {
1702 self.settings += 1;
1703 if self.settings > MAX_SETTINGS_FRAMES {
1704 return Err(Error::BadResponse(format!(
1705 "http2: peer sent {} SETTINGS frames (flood)",
1706 self.settings
1707 )));
1708 }
1709 }
1710 F_PING if flags & FLAG_ACK == 0 => {
1711 self.ping += 1;
1712 if self.ping > MAX_PING_FRAMES {
1713 return Err(Error::BadResponse(format!(
1714 "http2: peer sent {} PING frames (flood)",
1715 self.ping
1716 )));
1717 }
1718 }
1719 F_RST_STREAM => {
1720 self.rst_stream += 1;
1721 if self.rst_stream > MAX_RST_STREAM_FRAMES {
1722 return Err(Error::BadResponse(format!(
1723 "http2: peer sent {} RST_STREAM frames (Rapid-Reset flood)",
1724 self.rst_stream
1725 )));
1726 }
1727 }
1728 _ => {}
1729 }
1730 Ok(())
1731 }
1732
1733 fn record_progress(&mut self, made_progress: bool) -> Result<()> {
1740 if made_progress {
1741 self.no_progress = 0;
1742 } else {
1743 self.no_progress += 1;
1744 if self.no_progress > MAX_NO_PROGRESS_FRAMES {
1745 return Err(Error::BadResponse(format!(
1746 "http2: peer sent {} consecutive frames with no forward progress (flood)",
1747 self.no_progress
1748 )));
1749 }
1750 }
1751 Ok(())
1752 }
1753}
1754
1755#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1761enum DispatchOutcome {
1762 Continue,
1763 Done(u32),
1764}
1765
1766impl<S: Read + Write> Connection<S> {
1767 fn new(mut tls: S, recv_window: u32) -> Result<Self> {
1777 let recv_window = recv_window.clamp(OUR_INITIAL_WINDOW as u32, INITIAL_WINDOW_SIZE_MAX);
1778 tls.write_all(PREFACE)?;
1779 let mut settings_payload = Vec::with_capacity(12);
1780 settings_payload.extend_from_slice(&S_ENABLE_PUSH.to_be_bytes());
1781 settings_payload.extend_from_slice(&0u32.to_be_bytes());
1782 settings_payload.extend_from_slice(&S_INITIAL_WINDOW_SIZE.to_be_bytes());
1783 settings_payload.extend_from_slice(&recv_window.to_be_bytes());
1784 let our_settings = Frame {
1785 typ: F_SETTINGS,
1786 flags: 0,
1787 stream_id: 0,
1788 payload: settings_payload,
1789 };
1790 write_frame(&mut tls, &our_settings)?;
1791 let conn_bump = recv_window - OUR_INITIAL_WINDOW as u32;
1796 if conn_bump > 0 {
1797 write_frame(&mut tls, &window_update_frame(0, conn_bump))?;
1798 }
1799 tls.flush()?;
1800 Ok(Connection {
1801 tls,
1802 peer: PeerSettings::default(),
1803 conn_send_window: ConnSendWindow::new(),
1804 conn_recv_window: ConnRecvWindow::new(recv_window as i64),
1805 our_recv_window: recv_window as i64,
1806 decoder: Decoder::new(),
1807 encoder: Encoder::new(),
1808 streams: HashMap::new(),
1809 next_stream_id: 1,
1810 goaway_received: None,
1811 expecting_continuation: None,
1812 budget: FloodBudget::default(),
1813 made_progress: false,
1814 tls_info: None,
1815 dial_timing: crate::http::Timing::default(),
1816 })
1817 }
1818
1819 fn is_usable(&self) -> bool {
1833 if self.goaway_received.is_some() {
1834 return false;
1835 }
1836 if !self.streams.is_empty() {
1837 return false;
1838 }
1839 if self.next_stream_id >= 0x8000_0000 {
1842 return false;
1843 }
1844 true
1845 }
1846
1847 fn prune_completed_streams(&mut self) {
1856 self.streams.retain(|_, s| {
1857 let terminal = matches!(s.state, StreamState::Closed)
1858 || (matches!(s.state, StreamState::HalfClosedRemote)
1859 && s.response_headers.is_some()
1860 && s.end_stream_recv);
1861 !terminal
1862 });
1863 }
1864
1865 fn open_stream(&mut self) -> Result<u32> {
1877 if (self.streams.len() as u64) >= self.peer.max_concurrent_streams as u64 {
1878 return Err(Error::BadResponse("at MAX_CONCURRENT_STREAMS limit".into()));
1879 }
1880 if self.next_stream_id >= 0x8000_0000 {
1883 return Err(Error::BadResponse(
1884 "stream id space exhausted (RFC 9113 §5.1.1)".into(),
1885 ));
1886 }
1887 if let Some(last) = self.goaway_received {
1888 if self.next_stream_id > last {
1889 return Err(Error::BadResponse(format!(
1890 "GOAWAY received with last-stream-id={last}; cannot allocate id={}",
1891 self.next_stream_id
1892 )));
1893 }
1894 }
1895 let id = self.next_stream_id;
1896 self.next_stream_id = self.next_stream_id.saturating_add(2);
1897 self.streams.insert(
1898 id,
1899 Stream::new(
1900 id,
1901 self.peer.initial_window_size as i64,
1902 self.our_recv_window,
1903 ),
1904 );
1905 Ok(id)
1906 }
1907
1908 fn send_priority_hint(
1913 &mut self,
1914 stream_id: u32,
1915 priority: crate::http::Priority,
1916 ) -> Result<()> {
1917 let Some(weight) = priority_weight_byte(priority) else {
1918 return Ok(());
1919 };
1920 let payload = vec![0, 0, 0, 0, weight];
1922 write_frame(
1923 &mut self.tls,
1924 &Frame {
1925 typ: F_PRIORITY,
1926 flags: 0,
1927 stream_id,
1928 payload,
1929 },
1930 )?;
1931 Ok(())
1932 }
1933
1934 fn send_request_on(&mut self, stream_id: u32, req: &Request) -> Result<()> {
1939 self.send_priority_hint(stream_id, req.priority)?;
1940 let header_block = build_header_block(&mut self.encoder, req);
1941 let has_body = !req.body.is_empty();
1942 let max_frame_size = self.peer.max_frame_size as usize;
1943 let header_frames =
1944 fragment_header_block(stream_id, &header_block, max_frame_size, !has_body);
1945
1946 for f in &header_frames {
1950 write_frame(&mut self.tls, f)?;
1951 }
1952 {
1953 let s = self
1954 .streams
1955 .get_mut(&stream_id)
1956 .ok_or_else(|| Error::BadResponse(format!("stream {stream_id} not found")))?;
1957 s.state = s.state.send_data(!has_body)?;
1958 }
1959
1960 if has_body {
1961 let mut remaining: &[u8] = req.body.as_slice();
1962 while !remaining.is_empty() {
1963 loop {
1965 let budget = {
1966 let s = self.streams.get(&stream_id).ok_or_else(|| {
1967 Error::BadResponse(format!("stream {stream_id} disappeared mid-send"))
1968 })?;
1969 s.send_budget(&self.conn_send_window)
1970 };
1971 if budget > 0 {
1972 break;
1973 }
1974 match self.read_and_dispatch(None)? {
1975 DispatchOutcome::Continue => {}
1976 DispatchOutcome::Done(done_id) if done_id == stream_id => {
1977 return Err(Error::BadResponse(
1980 "server ended stream before request body was fully sent".into(),
1981 ));
1982 }
1983 DispatchOutcome::Done(_) => {
1984 }
1987 }
1988 }
1989
1990 let max_frame_size = self.peer.max_frame_size as usize;
1991 let budget = self
1992 .streams
1993 .get(&stream_id)
1994 .unwrap()
1995 .send_budget(&self.conn_send_window);
1996 let n = next_data_chunk_size(max_frame_size, budget, remaining.len());
1997 debug_assert!(n > 0, "loop above guarantees positive budget");
1998 let chunk = &remaining[..n];
1999 remaining = &remaining[n..];
2000 let is_last = remaining.is_empty();
2001 let data_frame = Frame {
2002 typ: F_DATA,
2003 flags: if is_last { FLAG_END_STREAM } else { 0 },
2004 stream_id,
2005 payload: chunk.to_vec(),
2006 };
2007 write_frame(&mut self.tls, &data_frame)?;
2008 self.conn_send_window.consume(n);
2009 let s = self.streams.get_mut(&stream_id).unwrap();
2010 s.send_window.consume(n);
2011 s.state = s.state.send_data(is_last)?;
2012 }
2013 }
2014 self.tls.flush()?;
2015 Ok(())
2016 }
2017
2018 fn drive_until_stream_done(&mut self, stream_id: u32) -> Result<Stream> {
2021 self.drive_until_stream_done_to(stream_id, None, None)
2022 }
2023
2024 fn drive_until_stream_done_to(
2029 &mut self,
2030 stream_id: u32,
2031 mut sink: Option<&mut dyn Write>,
2032 mut on_head: Option<crate::http::HeadObserver<'_>>,
2033 ) -> Result<Stream> {
2034 loop {
2035 if let Some(s) = self.streams.get(&stream_id) {
2037 if matches!(s.state, StreamState::Closed | StreamState::HalfClosedRemote)
2038 && s.response_headers.is_some()
2039 && s.end_stream_recv
2040 {
2041 self.fire_head(stream_id, &mut on_head);
2042 return Ok(self.streams.remove(&stream_id).unwrap());
2043 }
2044 } else {
2045 return Err(Error::BadResponse(format!(
2046 "stream {stream_id} not registered"
2047 )));
2048 }
2049
2050 let reborrow: Option<&mut dyn Write> = match &mut sink {
2051 Some(w) => Some(&mut **w),
2052 None => None,
2053 };
2054 let outcome = self.read_and_dispatch(reborrow)?;
2055 self.fire_head(stream_id, &mut on_head);
2060 match outcome {
2061 DispatchOutcome::Continue => {}
2062 DispatchOutcome::Done(done_id) if done_id == stream_id => {
2063 return Ok(self.streams.remove(&stream_id).unwrap());
2064 }
2065 DispatchOutcome::Done(_) => {
2066 }
2068 }
2069 }
2070 }
2071
2072 fn fire_head(&self, stream_id: u32, on_head: &mut Option<crate::http::HeadObserver<'_>>) {
2076 if on_head.is_none() {
2077 return;
2078 }
2079 let Some(s) = self.streams.get(&stream_id) else {
2080 return;
2081 };
2082 let Some(headers) = s.response_headers.as_ref() else {
2083 return;
2084 };
2085 let mut status: Option<u16> = None;
2086 let mut clean: Vec<(String, String)> = Vec::with_capacity(headers.len());
2087 for (k, v) in headers {
2088 if k == ":status" {
2089 status = v.parse::<u16>().ok();
2090 } else if !k.starts_with(':') {
2091 clean.push((k.clone(), v.clone()));
2092 }
2093 }
2094 let Some(status) = status.filter(|s| *s >= 200) else {
2096 return;
2097 };
2098 if let Some(obs) = on_head.take() {
2099 obs(&crate::http::ResponseHead {
2100 status,
2101 reason: String::new(),
2102 version: "HTTP/2".to_string(),
2103 headers: clean,
2104 });
2105 }
2106 }
2107
2108 fn stage_request_on(&mut self, stream_id: u32, req: &Request) -> Result<()> {
2130 self.send_priority_hint(stream_id, req.priority)?;
2131 let header_block = build_header_block(&mut self.encoder, req);
2132 let has_body = !req.body.is_empty();
2133 let max_frame_size = self.peer.max_frame_size as usize;
2134 let header_frames =
2135 fragment_header_block(stream_id, &header_block, max_frame_size, !has_body);
2136 for f in &header_frames {
2137 write_frame(&mut self.tls, f)?;
2138 }
2139 let s = self
2140 .streams
2141 .get_mut(&stream_id)
2142 .ok_or_else(|| Error::BadResponse(format!("stream {stream_id} not found")))?;
2143 s.state = s.state.send_data(!has_body)?;
2144 if has_body {
2145 s.pending_body = Some(PendingBody {
2146 bytes: req.body.clone(),
2147 sent: 0,
2148 });
2149 }
2150 Ok(())
2151 }
2152
2153 fn pump_pending_sends(&mut self, trace: &mut dyn Write) -> Result<bool> {
2166 let mut wrote = false;
2167 let max_frame_size = self.peer.max_frame_size as usize;
2168 let mut ids: Vec<u32> = self
2171 .streams
2172 .iter()
2173 .filter(|(_, s)| s.pending_body.is_some())
2174 .map(|(id, _)| *id)
2175 .collect();
2176 ids.sort_unstable();
2177
2178 for id in ids {
2179 if !self.streams.contains_key(&id) {
2183 continue;
2184 }
2185 loop {
2186 let s = self.streams.get(&id).unwrap();
2190 let budget = s.send_budget(&self.conn_send_window);
2191 let (remaining_len, sent) = match s.pending_body.as_ref() {
2192 Some(pb) => (pb.bytes.len() - pb.sent, pb.sent),
2193 None => break, };
2195 if remaining_len == 0 {
2196 self.streams.get_mut(&id).unwrap().pending_body = None;
2199 break;
2200 }
2201 let n = next_data_chunk_size(max_frame_size, budget, remaining_len);
2202 if n == 0 {
2203 break;
2206 }
2207 let is_last = n == remaining_len;
2208 let chunk: Vec<u8> = {
2209 let pb = self
2210 .streams
2211 .get(&id)
2212 .unwrap()
2213 .pending_body
2214 .as_ref()
2215 .unwrap();
2216 pb.bytes[sent..sent + n].to_vec()
2217 };
2218 let data_frame = Frame {
2219 typ: F_DATA,
2220 flags: if is_last { FLAG_END_STREAM } else { 0 },
2221 stream_id: id,
2222 payload: chunk,
2223 };
2224 write_frame(&mut self.tls, &data_frame)?;
2225 self.conn_send_window.consume(n);
2226 let s = self.streams.get_mut(&id).unwrap();
2227 s.send_window.consume(n);
2228 s.state = s.state.send_data(is_last)?;
2229 let pb = s.pending_body.as_mut().unwrap();
2230 pb.sent += n;
2231 if is_last {
2232 s.pending_body = None;
2233 let _ = writeln!(trace, "* [stream {id}] request body sent");
2234 }
2235 wrote = true;
2236 if is_last {
2237 break;
2238 }
2239 }
2240 }
2241 Ok(wrote)
2242 }
2243
2244 fn run_multiplexed(
2267 &mut self,
2268 reqs: &[Request],
2269 trace: &mut dyn Write,
2270 ) -> Vec<Result<Response>> {
2271 let n = reqs.len();
2272 let mut results: Vec<Option<Result<Response>>> = (0..n).map(|_| None).collect();
2275 let mut id_to_idx: HashMap<u32, usize> = HashMap::new();
2278 let mut queue: VecDeque<usize> = (0..n).collect();
2280
2281 self.start_queued(&mut queue, &mut id_to_idx, &mut results, reqs, trace);
2288 match self.pump_pending_sends(trace) {
2290 Ok(_) => {}
2291 Err(e) => {
2292 self.fail_all_outstanding(&id_to_idx, &mut results, &queue, &e);
2295 return collect_results(results);
2296 }
2297 }
2298 if let Err(e) = self.tls.flush() {
2299 let e = Error::Io(e);
2300 self.fail_all_outstanding(&id_to_idx, &mut results, &queue, &e);
2301 return collect_results(results);
2302 }
2303
2304 while results.iter().any(Option::is_none) {
2306 if id_to_idx.is_empty() {
2310 if queue.is_empty() {
2311 break;
2312 }
2313 self.start_queued(&mut queue, &mut id_to_idx, &mut results, reqs, trace);
2316 if id_to_idx.is_empty() {
2317 while let Some(idx) = queue.pop_front() {
2318 if results[idx].is_none() {
2319 results[idx] = Some(Err(Error::BadResponse(
2320 "no usable stream to issue request (GOAWAY?)".into(),
2321 )));
2322 }
2323 }
2324 break;
2325 }
2326 if self.flush_pending(trace).is_err() {
2327 break;
2328 }
2329 }
2330
2331 let outcome = match self.read_and_dispatch(None) {
2333 Ok(o) => o,
2334 Err(e) => {
2335 if let Some(idx) = self.take_stream_error(&mut id_to_idx) {
2341 results[idx] = Some(Err(e));
2342 self.start_queued(&mut queue, &mut id_to_idx, &mut results, reqs, trace);
2344 if let Err(fe) = self.flush_pending(trace) {
2345 self.fail_all_outstanding(&id_to_idx, &mut results, &queue, &fe);
2346 break;
2347 }
2348 continue;
2349 }
2350 self.fail_all_outstanding(&id_to_idx, &mut results, &queue, &e);
2352 break;
2353 }
2354 };
2355
2356 if self.goaway_received.is_some() {
2359 self.fail_goaway_doomed(&mut id_to_idx, &mut results);
2360 }
2361
2362 if let DispatchOutcome::Done(done_id) = outcome {
2363 if let Some(idx) = id_to_idx.remove(&done_id) {
2364 let stream = self
2365 .streams
2366 .remove(&done_id)
2367 .expect("Done stream must still be registered");
2368 let mut built = build_response_from_stream_labelled(
2369 stream,
2370 Some(done_id),
2371 reqs[idx].decompress,
2372 trace,
2373 );
2374 if let Ok(resp) = &mut built {
2375 resp.tls = self.tls_info.clone();
2376 }
2377 results[idx] = Some(built);
2378 self.start_queued(&mut queue, &mut id_to_idx, &mut results, reqs, trace);
2380 }
2381 }
2382
2383 if self.flush_pending(trace).is_err() {
2386 let e = Error::BadResponse("write error pumping multiplexed sends".into());
2387 self.fail_all_outstanding(&id_to_idx, &mut results, &queue, &e);
2388 break;
2389 }
2390 }
2391
2392 self.prune_completed_streams();
2393 collect_results(results)
2394 }
2395
2396 fn flush_pending(&mut self, trace: &mut dyn Write) -> Result<()> {
2399 self.pump_pending_sends(trace)?;
2400 self.tls.flush().map_err(Error::Io)
2401 }
2402
2403 fn start_queued(
2408 &mut self,
2409 queue: &mut VecDeque<usize>,
2410 id_to_idx: &mut HashMap<u32, usize>,
2411 results: &mut [Option<Result<Response>>],
2412 reqs: &[Request],
2413 trace: &mut dyn Write,
2414 ) {
2415 while !queue.is_empty() {
2416 if (self.streams.len() as u64) >= self.peer.max_concurrent_streams as u64 {
2418 break;
2419 }
2420 let idx = *queue.front().unwrap();
2421 let id = match self.open_stream() {
2422 Ok(id) => id,
2423 Err(e) => {
2424 queue.pop_front();
2429 results[idx] = Some(Err(e));
2430 continue;
2431 }
2432 };
2433 queue.pop_front();
2434 let req = &reqs[idx];
2435 trace_request_labelled(req, id, trace);
2436 if let Err(e) = self.stage_request_on(id, req) {
2437 self.streams.remove(&id);
2439 results[idx] = Some(Err(e));
2440 continue;
2441 }
2442 id_to_idx.insert(id, idx);
2443 }
2444 }
2445
2446 fn take_stream_error(&mut self, id_to_idx: &mut HashMap<u32, usize>) -> Option<usize> {
2452 let mut culprit: Option<u32> = None;
2453 for (&id, s) in self.streams.iter() {
2454 if !id_to_idx.contains_key(&id) {
2455 continue;
2456 }
2457 let complete = matches!(s.state, StreamState::Closed | StreamState::HalfClosedRemote)
2458 && s.end_stream_recv
2459 && s.response_headers.is_some();
2460 if matches!(s.state, StreamState::Closed) && !complete {
2461 if culprit.is_some() {
2462 return None;
2464 }
2465 culprit = Some(id);
2466 }
2467 }
2468 let id = culprit?;
2469 let idx = id_to_idx.remove(&id)?;
2470 self.streams.remove(&id);
2471 Some(idx)
2472 }
2473
2474 fn fail_goaway_doomed(
2479 &mut self,
2480 id_to_idx: &mut HashMap<u32, usize>,
2481 results: &mut [Option<Result<Response>>],
2482 ) {
2483 let last = match self.goaway_received {
2484 Some(l) => l,
2485 None => return,
2486 };
2487 let doomed: Vec<u32> = id_to_idx
2488 .keys()
2489 .copied()
2490 .filter(|id| *id > last)
2491 .filter(|id| {
2492 match self.streams.get(id) {
2494 Some(s) => !(s.end_stream_recv && s.response_headers.is_some()),
2495 None => true,
2496 }
2497 })
2498 .collect();
2499 for id in doomed {
2500 if let Some(idx) = id_to_idx.remove(&id) {
2501 self.streams.remove(&id);
2502 results[idx] = Some(Err(Error::BadResponse(format!(
2503 "stream {id} abandoned by GOAWAY (last-stream-id={last})"
2504 ))));
2505 }
2506 }
2507 }
2508
2509 fn fail_all_outstanding(
2512 &self,
2513 id_to_idx: &HashMap<u32, usize>,
2514 results: &mut [Option<Result<Response>>],
2515 queue: &VecDeque<usize>,
2516 err: &Error,
2517 ) {
2518 for &idx in id_to_idx.values() {
2519 if results[idx].is_none() {
2520 results[idx] = Some(Err(clone_error(err)));
2521 }
2522 }
2523 for &idx in queue.iter() {
2524 if results[idx].is_none() {
2525 results[idx] = Some(Err(clone_error(err)));
2526 }
2527 }
2528 }
2529
2530 fn read_and_dispatch(&mut self, sink: Option<&mut dyn Write>) -> Result<DispatchOutcome> {
2535 let frame = match read_frame(&mut self.tls) {
2536 Ok(f) => f,
2537 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
2538 return Err(Error::UnexpectedEof);
2539 }
2540 Err(e) => return Err(Error::Io(e)),
2541 };
2542 self.process_frame(frame, sink)
2543 }
2544
2545 fn process_frame(
2548 &mut self,
2549 frame: Frame,
2550 sink: Option<&mut dyn Write>,
2551 ) -> Result<DispatchOutcome> {
2552 if let Some(awaiting) = self.expecting_continuation {
2557 let ok = frame.typ == F_CONTINUATION && frame.stream_id == awaiting;
2558 if !ok {
2559 return Err(Error::BadResponse(format!(
2560 "expected CONTINUATION on stream {awaiting}, got type=0x{:x} stream={}",
2561 frame.typ, frame.stream_id
2562 )));
2563 }
2564 }
2565
2566 let frame_typ = frame.typ;
2571 let frame_flags = frame.flags;
2572 self.budget.record_control_frame(frame_typ, frame_flags)?;
2576 self.made_progress = false;
2581 let outcome = if frame.stream_id == 0 {
2582 self.process_conn_frame(frame)
2583 } else {
2584 self.process_stream_frame(frame, sink)
2585 }?;
2586 let progress = self.made_progress || matches!(outcome, DispatchOutcome::Done(_));
2587 self.budget.record_progress(progress)?;
2588 Ok(outcome)
2589 }
2590
2591 fn process_conn_frame(&mut self, frame: Frame) -> Result<DispatchOutcome> {
2595 match frame.typ {
2596 F_SETTINGS if frame.flags & FLAG_ACK == 0 => {
2597 let old_initial = self.peer.initial_window_size;
2598 let old_header_table_size = self.peer.header_table_size;
2599 self.peer.apply_settings_payload(&frame.payload)?;
2600 let new_initial = self.peer.initial_window_size;
2601 if new_initial != old_initial {
2602 for s in self.streams.values_mut() {
2606 s.send_window.apply_initial_window_change(new_initial)?;
2607 }
2608 }
2609 if self.peer.header_table_size != old_header_table_size {
2610 self.encoder
2615 .set_peer_max_table_size(self.peer.header_table_size as usize);
2616 }
2617 let ack = Frame {
2618 typ: F_SETTINGS,
2619 flags: FLAG_ACK,
2620 stream_id: 0,
2621 payload: Vec::new(),
2622 };
2623 write_frame(&mut self.tls, &ack)?;
2624 self.tls.flush()?;
2625 }
2626 F_SETTINGS => { }
2627 F_PING if frame.flags & FLAG_ACK == 0 => {
2628 let pong = Frame {
2629 typ: F_PING,
2630 flags: FLAG_ACK,
2631 stream_id: 0,
2632 payload: frame.payload.clone(),
2633 };
2634 write_frame(&mut self.tls, &pong)?;
2635 self.tls.flush()?;
2636 }
2637 F_PING => {}
2638 F_WINDOW_UPDATE => {
2639 let inc = parse_window_update(&frame.payload)?;
2640 self.conn_send_window.apply_window_update(inc)?;
2641 self.made_progress = true;
2644 }
2645 F_GOAWAY => {
2646 let last = if frame.payload.len() >= 4 {
2652 u32::from_be_bytes([
2653 frame.payload[0],
2654 frame.payload[1],
2655 frame.payload[2],
2656 frame.payload[3],
2657 ]) & 0x7fff_ffff
2658 } else {
2659 0
2660 };
2661 self.goaway_received = Some(last);
2662 let doomed: Vec<u32> = self
2667 .streams
2668 .iter()
2669 .filter(|(id, _)| **id > last)
2670 .map(|(id, _)| *id)
2671 .collect();
2672 for id in doomed {
2673 if let Some(s) = self.streams.get_mut(&id) {
2674 s.state = StreamState::Closed;
2675 }
2676 }
2677 }
2678 _ => {
2679 }
2683 }
2684 Ok(DispatchOutcome::Continue)
2685 }
2686
2687 fn process_stream_frame(
2690 &mut self,
2691 frame: Frame,
2692 sink: Option<&mut dyn Write>,
2693 ) -> Result<DispatchOutcome> {
2694 match frame.typ {
2695 F_HEADERS => self.process_headers(frame),
2696 F_CONTINUATION => self.process_continuation(frame),
2697 F_DATA => self.process_data(frame, sink),
2698 F_RST_STREAM => self.process_rst(frame),
2699 F_WINDOW_UPDATE => {
2700 let inc = parse_window_update(&frame.payload)?;
2701 if let Some(s) = self.streams.get_mut(&frame.stream_id) {
2702 s.send_window.apply_window_update(inc)?;
2703 self.made_progress = true;
2708 }
2709 Ok(DispatchOutcome::Continue)
2711 }
2712 F_PUSH_PROMISE => {
2713 Err(Error::BadResponse(
2716 "received PUSH_PROMISE despite SETTINGS_ENABLE_PUSH=0".into(),
2717 ))
2718 }
2719 _ => {
2720 Ok(DispatchOutcome::Continue)
2722 }
2723 }
2724 }
2725
2726 fn process_headers(&mut self, frame: Frame) -> Result<DispatchOutcome> {
2727 let mut payload = frame.payload.as_slice();
2730 let mut pad_len = 0usize;
2731 if frame.flags & FLAG_PADDED != 0 {
2732 if payload.is_empty() {
2733 return Err(Error::BadResponse(
2734 "HEADERS PADDED with empty payload".into(),
2735 ));
2736 }
2737 pad_len = payload[0] as usize;
2738 payload = &payload[1..];
2739 }
2740 if frame.flags & FLAG_PRIORITY != 0 {
2741 if payload.len() < 5 {
2742 return Err(Error::BadResponse(
2743 "HEADERS PRIORITY with insufficient payload".into(),
2744 ));
2745 }
2746 payload = &payload[5..];
2747 }
2748 if payload.len() < pad_len {
2749 return Err(Error::BadResponse(
2750 "HEADERS padding overruns payload".into(),
2751 ));
2752 }
2753 let frag = &payload[..payload.len() - pad_len];
2754 let end_headers = frame.flags & FLAG_END_HEADERS != 0;
2755 let end_stream = frame.flags & FLAG_END_STREAM != 0;
2756
2757 let stream_id = frame.stream_id;
2758 let known = self.streams.contains_key(&stream_id);
2761 if !known {
2762 return Err(Error::BadResponse(format!(
2763 "HEADERS on unknown stream {stream_id} (server push disabled)"
2764 )));
2765 }
2766
2767 let state = self.streams.get(&stream_id).unwrap().state;
2770 if state == StreamState::Closed {
2771 if end_headers {
2780 let _ = self.decoder.decode_block(frag)?;
2781 } else {
2782 self.streams
2785 .get_mut(&stream_id)
2786 .unwrap()
2787 .push_header_fragment(frag)?;
2788 self.expecting_continuation = Some(stream_id);
2789 }
2790 return Ok(DispatchOutcome::Continue);
2791 }
2792 let new_state = state.recv_headers(end_stream)?;
2793
2794 let s = self.streams.get_mut(&stream_id).unwrap();
2795 s.push_header_fragment(frag)?;
2796 if end_stream {
2797 s.end_stream_recv = true;
2798 }
2799 if end_headers {
2800 let block = std::mem::take(&mut s.headers_buf);
2802 let decoded = self.decoder.decode_block(&block)?;
2804 let s = self.streams.get_mut(&stream_id).unwrap();
2805 s.response_headers = Some(decoded);
2806 s.state = new_state;
2807 self.expecting_continuation = None;
2808 self.made_progress = true;
2810 } else {
2811 s.state = new_state;
2812 self.expecting_continuation = Some(stream_id);
2813 }
2814
2815 let done = matches!(
2816 self.streams.get(&stream_id).unwrap().state,
2817 StreamState::Closed | StreamState::HalfClosedRemote
2818 ) && self.streams.get(&stream_id).unwrap().end_stream_recv
2819 && self
2820 .streams
2821 .get(&stream_id)
2822 .unwrap()
2823 .response_headers
2824 .is_some();
2825 Ok(if done {
2826 DispatchOutcome::Done(stream_id)
2827 } else {
2828 DispatchOutcome::Continue
2829 })
2830 }
2831
2832 fn process_continuation(&mut self, frame: Frame) -> Result<DispatchOutcome> {
2833 let stream_id = frame.stream_id;
2834 match self.expecting_continuation {
2836 Some(awaiting) if awaiting == stream_id => {}
2837 _ => {
2838 return Err(Error::BadResponse(format!(
2839 "unexpected CONTINUATION on stream {stream_id}"
2840 )));
2841 }
2842 }
2843 let s = self.streams.get_mut(&stream_id).ok_or_else(|| {
2844 Error::BadResponse(format!("CONTINUATION on unknown stream {stream_id}"))
2845 })?;
2846 s.push_header_fragment(&frame.payload)?;
2847 let end_headers = frame.flags & FLAG_END_HEADERS != 0;
2848 if end_headers {
2849 let block = std::mem::take(&mut s.headers_buf);
2850 let decoded = self.decoder.decode_block(&block)?;
2851 let s = self.streams.get_mut(&stream_id).unwrap();
2852 if s.state != StreamState::Closed {
2853 s.response_headers = Some(decoded);
2854 }
2855 self.expecting_continuation = None;
2856 self.made_progress = true;
2858 }
2859 let done = matches!(
2860 self.streams.get(&stream_id).unwrap().state,
2861 StreamState::Closed | StreamState::HalfClosedRemote
2862 ) && self.streams.get(&stream_id).unwrap().end_stream_recv
2863 && self
2864 .streams
2865 .get(&stream_id)
2866 .unwrap()
2867 .response_headers
2868 .is_some();
2869 Ok(if done {
2870 DispatchOutcome::Done(stream_id)
2871 } else {
2872 DispatchOutcome::Continue
2873 })
2874 }
2875
2876 fn process_data(
2877 &mut self,
2878 frame: Frame,
2879 sink: Option<&mut dyn Write>,
2880 ) -> Result<DispatchOutcome> {
2881 let stream_id = frame.stream_id;
2882 let frame_bytes = frame.payload.len();
2883 self.conn_recv_window.consume(frame_bytes);
2886 if self.conn_recv_window.available < 0 {
2892 return Err(Error::BadResponse(
2893 "http2: flow-control window exceeded by peer".into(),
2894 ));
2895 }
2896 let known = self.streams.contains_key(&stream_id);
2897 if !known {
2898 if let Some(upd) = self.conn_recv_window.replenish() {
2903 write_frame(&mut self.tls, &upd)?;
2904 self.tls.flush()?;
2905 }
2906 return Ok(DispatchOutcome::Continue);
2907 }
2908 let state = self.streams.get(&stream_id).unwrap().state;
2909 if state == StreamState::Closed {
2910 return Err(Error::BadResponse(format!(
2913 "DATA on closed stream {stream_id}"
2914 )));
2915 }
2916 let end_stream = frame.flags & FLAG_END_STREAM != 0;
2917 let new_state = state.recv_data(end_stream)?;
2918
2919 let s = self.streams.get_mut(&stream_id).unwrap();
2920 s.recv_window.consume(frame_bytes);
2921 if s.recv_window.available < 0 {
2925 return Err(Error::BadResponse(
2926 "http2: flow-control window exceeded by peer".into(),
2927 ));
2928 }
2929
2930 let mut payload = frame.payload.as_slice();
2932 if frame.flags & FLAG_PADDED != 0 {
2933 if payload.is_empty() {
2934 return Err(Error::BadResponse("DATA PADDED with empty payload".into()));
2935 }
2936 let pad_len = payload[0] as usize;
2937 payload = &payload[1..];
2938 if payload.len() < pad_len {
2939 return Err(Error::BadResponse("DATA padding overruns payload".into()));
2940 }
2941 payload = &payload[..payload.len() - pad_len];
2942 }
2943 let encoded = s.response_headers.as_ref().is_some_and(|h| {
2948 h.iter()
2949 .any(|(k, _)| k.eq_ignore_ascii_case("content-encoding"))
2950 });
2951 let to_sink = sink.is_some() && !encoded && s.body.is_empty();
2952 if !to_sink && s.body.len().saturating_add(payload.len()) > MAX_RESPONSE_BYTES {
2958 return Err(Error::BadResponse(
2959 "response body exceeds size limit".into(),
2960 ));
2961 }
2962 let appended_body = !payload.is_empty();
2968 if to_sink {
2969 if let Some(w) = sink {
2970 w.write_all(payload)?;
2971 }
2972 s.streamed_len += payload.len() as u64;
2973 } else {
2974 s.body.extend_from_slice(payload);
2975 }
2976 if end_stream {
2977 s.end_stream_recv = true;
2978 }
2979 s.state = new_state;
2980 if appended_body {
2981 self.made_progress = true;
2982 }
2983
2984 if let Some(upd) = self.conn_recv_window.replenish() {
2987 write_frame(&mut self.tls, &upd)?;
2988 }
2989 if let Some(upd) = self
2990 .streams
2991 .get_mut(&stream_id)
2992 .unwrap()
2993 .recv_window
2994 .replenish(stream_id)
2995 {
2996 write_frame(&mut self.tls, &upd)?;
2997 }
2998 self.tls.flush()?;
2999
3000 let s = self.streams.get(&stream_id).unwrap();
3001 let done = matches!(s.state, StreamState::Closed | StreamState::HalfClosedRemote)
3002 && s.end_stream_recv
3003 && s.response_headers.is_some();
3004 Ok(if done {
3005 DispatchOutcome::Done(stream_id)
3006 } else {
3007 DispatchOutcome::Continue
3008 })
3009 }
3010
3011 fn process_rst(&mut self, frame: Frame) -> Result<DispatchOutcome> {
3012 let stream_id = frame.stream_id;
3013 let code = if frame.payload.len() >= 4 {
3014 u32::from_be_bytes([
3015 frame.payload[0],
3016 frame.payload[1],
3017 frame.payload[2],
3018 frame.payload[3],
3019 ])
3020 } else {
3021 0
3022 };
3023 match self.streams.get_mut(&stream_id) {
3024 Some(s) => {
3025 if s.state == StreamState::Closed {
3027 return Ok(DispatchOutcome::Continue);
3028 }
3029 s.state = s.state.recv_rst()?;
3030 Err(Error::BadResponse(format!(
3031 "stream {stream_id} reset by server, error code {code}"
3032 )))
3033 }
3034 None => {
3035 Ok(DispatchOutcome::Continue)
3037 }
3038 }
3039 }
3040}
3041
3042fn fragment_header_block(
3061 stream_id: u32,
3062 header_block: &[u8],
3063 max_frame_size: usize,
3064 end_stream: bool,
3065) -> Vec<Frame> {
3066 debug_assert!(max_frame_size > 0, "max_frame_size must be > 0");
3067 let mut frames = Vec::new();
3068
3069 if header_block.is_empty() {
3070 let mut flags = FLAG_END_HEADERS;
3071 if end_stream {
3072 flags |= FLAG_END_STREAM;
3073 }
3074 frames.push(Frame {
3075 typ: F_HEADERS,
3076 flags,
3077 stream_id,
3078 payload: Vec::new(),
3079 });
3080 return frames;
3081 }
3082
3083 let total_chunks = header_block.len().div_ceil(max_frame_size);
3084 for (i, chunk) in header_block.chunks(max_frame_size).enumerate() {
3085 let is_last = i + 1 == total_chunks;
3086 if i == 0 {
3087 let mut flags = 0u8;
3088 if end_stream {
3089 flags |= FLAG_END_STREAM;
3090 }
3091 if is_last {
3092 flags |= FLAG_END_HEADERS;
3093 }
3094 frames.push(Frame {
3095 typ: F_HEADERS,
3096 flags,
3097 stream_id,
3098 payload: chunk.to_vec(),
3099 });
3100 } else {
3101 let flags = if is_last { FLAG_END_HEADERS } else { 0 };
3102 frames.push(Frame {
3103 typ: F_CONTINUATION,
3104 flags,
3105 stream_id,
3106 payload: chunk.to_vec(),
3107 });
3108 }
3109 }
3110 frames
3111}
3112
3113fn next_data_chunk_size(max_frame_size: usize, available: i64, remaining: usize) -> usize {
3120 if available <= 0 {
3121 return 0;
3122 }
3123 let cap_window = available.min(remaining as i64).min(max_frame_size as i64);
3124 cap_window as usize
3125}
3126
3127#[derive(Hash, Eq, PartialEq, Clone, Debug)]
3151pub(crate) struct PoolKey {
3152 scheme: String,
3153 host: String,
3154 port: u16,
3155 partition: Option<String>,
3158}
3159
3160impl PoolKey {
3161 fn from_request(req: &Request) -> Self {
3162 PoolKey {
3163 scheme: req.url.scheme.clone(),
3164 host: req.url.host.clone(),
3165 port: req.url.port,
3166 partition: req.partition_key.clone(),
3167 }
3168 }
3169}
3170
3171type PooledConn<S> = Arc<Mutex<Connection<S>>>;
3181
3182pub(crate) struct PoolInner<S: Read + Write> {
3185 entries: HashMap<PoolKey, Vec<PooledConn<S>>>,
3186}
3187
3188impl<S: Read + Write> PoolInner<S> {
3189 fn new() -> Self {
3190 PoolInner {
3191 entries: HashMap::new(),
3192 }
3193 }
3194
3195 fn checkout(&mut self, key: &PoolKey) -> Option<PooledConn<S>> {
3198 let bucket = self.entries.get_mut(key)?;
3199 let conn = bucket.pop();
3200 if bucket.is_empty() {
3201 self.entries.remove(key);
3202 }
3203 conn
3204 }
3205
3206 fn release(&mut self, key: PoolKey, conn: PooledConn<S>) {
3211 let total: usize = self.entries.values().map(Vec::len).sum();
3214 if total >= crate::pool::global_cap() {
3215 return;
3216 }
3217 let bucket = self.entries.entry(key).or_default();
3218 if bucket.len() >= crate::pool::per_key_cap() {
3219 return;
3220 }
3221 bucket.push(conn);
3222 }
3223
3224 #[cfg(test)]
3226 fn total_len(&self) -> usize {
3227 self.entries.values().map(Vec::len).sum()
3228 }
3229}
3230
3231static POOL: OnceLock<Mutex<PoolInner<TlsStream<TcpStream>>>> = OnceLock::new();
3235
3236fn global_pool() -> &'static Mutex<PoolInner<TlsStream<TcpStream>>> {
3237 POOL.get_or_init(|| Mutex::new(PoolInner::new()))
3238}
3239
3240type DialedH2 = (
3244 Connection<TlsStream<TcpStream>>,
3245 Option<crate::cancel::CancelGuard>,
3246);
3247
3248fn dial_h2(req: &Request, trace: &mut dyn Write) -> Result<DialedH2> {
3249 let start = std::time::Instant::now();
3254 let (tcp, cancel_guard, namelookup) = crate::http::tcp_connect_cancellable(req, trace)?;
3255 let connect = start.elapsed();
3256 if let Some(p) = req
3260 .proxy
3261 .as_ref()
3262 .filter(|_| !crate::http::proxy_bypassed(req))
3263 {
3264 crate::http::connect_tunnel(&tcp, &req.url, p, trace)?;
3265 }
3266 let opts = crate::http::tls_opts_from(req, &[b"h2"])?;
3267 let tls = crate::tls::connect_over_tls(tcp, &req.url.host, opts)?;
3268 let appconnect = start.elapsed();
3269 crate::http::write_tls_info(&tls, trace);
3270 let negotiated_h2 = tls.alpn_selected().map(|p| p == b"h2").unwrap_or(false);
3271 if !negotiated_h2 {
3272 return Err(Error::H2NotNegotiated);
3275 }
3276 let _ = writeln!(trace, "* using HTTP/2");
3277 let tls_info = crate::http::tls_info_from(&tls);
3278 let mut conn = Connection::new(tls, req.h2_recv_window)?;
3279 conn.tls_info = Some(tls_info);
3280 conn.dial_timing = crate::http::Timing {
3281 namelookup,
3282 connect: Some(connect),
3283 appconnect: Some(appconnect),
3284 pretransfer: Some(appconnect),
3285 ..Default::default()
3286 };
3287 Ok((conn, cancel_guard))
3288}
3289
3290fn pool_eligible(req: &Request) -> bool {
3294 req.verify_tls && req.ca_bundle.is_none()
3295}
3296
3297pub fn send(req: Request, trace: &mut dyn Write) -> Result<Response> {
3311 if req.url.scheme != "https" {
3312 return Err(Error::UnsupportedScheme(format!(
3314 "http/2 over {} not supported",
3315 req.url.scheme
3316 )));
3317 }
3318
3319 let key = PoolKey::from_request(&req);
3320 let eligible = pool_eligible(&req);
3321
3322 if eligible {
3330 let pooled = {
3331 let mut guard = global_pool().lock().unwrap_or_else(|e| e.into_inner());
3337 guard.checkout(&key)
3338 };
3339 if let Some(arc) = pooled {
3340 let mut conn_guard = arc.lock().unwrap_or_else(|e| e.into_inner());
3343 if conn_guard.is_usable() {
3344 let _ = writeln!(trace, "* Reusing existing connection from pool");
3345 match run_one_request(&mut conn_guard, &req, trace) {
3346 Ok(resp) => {
3347 let still_usable = conn_guard.is_usable();
3348 drop(conn_guard);
3349 if still_usable {
3350 let mut guard = global_pool().lock().unwrap_or_else(|e| e.into_inner());
3351 guard.release(key.clone(), arc);
3352 let _ = writeln!(trace, "* Connection kept alive (pooled)");
3353 } else {
3354 let _ = writeln!(trace, "* Connection closed");
3355 }
3356 return Ok(resp);
3357 }
3358 Err(_e) => {
3359 drop(conn_guard);
3364 let _ = writeln!(
3365 trace,
3366 "* Pooled connection unusable (request failed); reconnecting"
3367 );
3368 }
3369 }
3370 } else {
3371 let _ = writeln!(
3373 trace,
3374 "* Pooled connection unusable (connection closed); reconnecting"
3375 );
3376 }
3377 }
3378 }
3379
3380 let (mut fresh, _cancel_guard) = dial_h2(&req, trace)?;
3382 let mut resp = run_one_request(&mut fresh, &req, trace)?;
3383 apply_dial_timing(&mut resp, &fresh);
3384 if eligible && fresh.is_usable() {
3385 let arc = Arc::new(Mutex::new(fresh));
3386 let mut guard = global_pool().lock().unwrap_or_else(|e| e.into_inner());
3387 guard.release(key, arc);
3388 let _ = writeln!(trace, "* Connection kept alive (pooled)");
3389 } else {
3390 let _ = writeln!(trace, "* Connection closed");
3391 }
3392 Ok(resp)
3393}
3394
3395fn run_one_request<S: Read + Write>(
3398 conn: &mut Connection<S>,
3399 req: &Request,
3400 trace: &mut dyn Write,
3401) -> Result<Response> {
3402 let stream_id = conn.open_stream()?;
3403 trace_request(req, trace);
3406 conn.send_request_on(stream_id, req)?;
3407 if !req.body.is_empty() {
3408 let _ = writeln!(trace, "* uploading {} body bytes", req.body.len());
3409 }
3410 let stream = conn.drive_until_stream_done(stream_id)?;
3411 conn.prune_completed_streams();
3414 let mut resp = build_response_from_stream(stream, req.decompress, trace)?;
3415 resp.tls = conn.tls_info.clone();
3418 Ok(resp)
3419}
3420
3421fn build_response_from_stream(
3427 stream: Stream,
3428 decompress: bool,
3429 trace: &mut dyn Write,
3430) -> Result<Response> {
3431 build_response_from_stream_labelled(stream, None, decompress, trace)
3432}
3433
3434fn build_response_from_stream_labelled(
3438 stream: Stream,
3439 label_id: Option<u32>,
3440 decompress: bool,
3441 trace: &mut dyn Write,
3442) -> Result<Response> {
3443 let headers = stream
3444 .response_headers
3445 .ok_or_else(|| Error::BadResponse("response ended before any HEADERS frame".into()))?;
3446
3447 let mut status: Option<u16> = None;
3448 let mut clean_headers: Vec<(String, String)> = Vec::with_capacity(headers.len());
3449 for (k, v) in headers {
3450 if k == ":status" {
3451 status = Some(
3452 v.parse::<u16>()
3453 .map_err(|_| Error::BadResponse(format!("bad :status {v:?}")))?,
3454 );
3455 } else if k.starts_with(':') {
3456 } else {
3458 clean_headers.push((k, v));
3459 }
3460 }
3461 let status = status.ok_or_else(|| Error::BadResponse("response missing :status".into()))?;
3462
3463 let tag = match label_id {
3467 Some(id) => format!("[stream {id}] "),
3468 None => String::new(),
3469 };
3470 let _ = writeln!(trace, "< {tag}HTTP/2 {status}");
3471 for (k, v) in &clean_headers {
3472 let _ = writeln!(trace, "< {tag}{k}: {v}");
3473 }
3474 let _ = writeln!(trace, "< {tag}");
3475
3476 let wire_len = stream.body.len();
3477 let _ = writeln!(trace, "* {tag}Received {wire_len} body bytes");
3478
3479 let (clean_headers, body) =
3482 crate::http::maybe_decode_body(clean_headers, stream.body, decompress, trace)?;
3483
3484 Ok(Response {
3485 status,
3486 reason: String::new(), version: "HTTP/2".to_string(),
3488 headers: clean_headers,
3489 body,
3490 timing: crate::http::Timing::default(),
3491 final_url: String::new(),
3494 tls: None,
3495 })
3496}
3497
3498fn build_response_from_stream_streaming(
3505 stream: Stream,
3506 sink: &mut dyn Write,
3507 decompress: bool,
3508 trace: &mut dyn Write,
3509) -> Result<Response> {
3510 let headers = stream
3511 .response_headers
3512 .ok_or_else(|| Error::BadResponse("response ended before any HEADERS frame".into()))?;
3513
3514 let mut status: Option<u16> = None;
3515 let mut clean_headers: Vec<(String, String)> = Vec::with_capacity(headers.len());
3516 for (k, v) in headers {
3517 if k == ":status" {
3518 status = Some(
3519 v.parse::<u16>()
3520 .map_err(|_| Error::BadResponse(format!("bad :status {v:?}")))?,
3521 );
3522 } else if !k.starts_with(':') {
3523 clean_headers.push((k, v));
3524 }
3525 }
3526 let status = status.ok_or_else(|| Error::BadResponse("response missing :status".into()))?;
3527
3528 let _ = writeln!(trace, "< HTTP/2 {status}");
3529 for (k, v) in &clean_headers {
3530 let _ = writeln!(trace, "< {k}: {v}");
3531 }
3532 let _ = writeln!(trace, "< ");
3533 let total = stream.body.len() as u64 + stream.streamed_len;
3534 let _ = writeln!(trace, "* Received {total} body bytes (streamed)");
3535
3536 let (clean_headers, body) =
3539 crate::http::maybe_decode_body(clean_headers, stream.body, decompress, trace)?;
3540 if !body.is_empty() {
3541 sink.write_all(&body)?;
3542 }
3543
3544 Ok(Response {
3545 status,
3546 reason: String::new(),
3547 version: "HTTP/2".to_string(),
3548 headers: clean_headers,
3549 body: Vec::new(),
3550 timing: crate::http::Timing::default(),
3551 final_url: String::new(),
3552 tls: None,
3553 })
3554}
3555
3556fn run_one_request_to<S: Read + Write>(
3560 conn: &mut Connection<S>,
3561 req: &Request,
3562 sink: &mut dyn Write,
3563 on_head: Option<crate::http::HeadObserver<'_>>,
3564 trace: &mut dyn Write,
3565) -> Result<Response> {
3566 let stream_id = conn.open_stream()?;
3567 trace_request(req, trace);
3568 conn.send_request_on(stream_id, req)?;
3569 let stream = conn.drive_until_stream_done_to(stream_id, Some(sink), on_head)?;
3570 conn.prune_completed_streams();
3571 let mut resp = build_response_from_stream_streaming(stream, sink, req.decompress, trace)?;
3572 resp.tls = conn.tls_info.clone();
3573 Ok(resp)
3574}
3575
3576pub fn send_to(
3580 req: Request,
3581 sink: &mut dyn Write,
3582 on_head: Option<crate::http::HeadObserver<'_>>,
3583 trace: &mut dyn Write,
3584) -> Result<Response> {
3585 if req.url.scheme != "https" {
3586 return Err(Error::UnsupportedScheme(format!(
3587 "http/2 over {} not supported",
3588 req.url.scheme
3589 )));
3590 }
3591 let (mut fresh, _cancel_guard) = dial_h2(&req, trace)?;
3592 let mut resp = run_one_request_to(&mut fresh, &req, sink, on_head, trace)?;
3593 apply_dial_timing(&mut resp, &fresh);
3594 let _ = writeln!(trace, "* Connection closed");
3595 Ok(resp)
3596}
3597
3598fn apply_dial_timing<S: Read + Write>(resp: &mut Response, conn: &Connection<S>) {
3601 resp.timing.namelookup = conn.dial_timing.namelookup;
3602 resp.timing.connect = conn.dial_timing.connect;
3603 resp.timing.appconnect = conn.dial_timing.appconnect;
3604 resp.timing.pretransfer = conn.dial_timing.pretransfer;
3605}
3606
3607fn request_header_fields(req: &Request) -> (RequestPseudo, Vec<(String, String)>) {
3620 let authority = if req.url.port == 443 && req.url.scheme == "https" {
3621 req.url.host.clone()
3622 } else {
3623 format!("{}:{}", req.url.host, req.url.port)
3624 };
3625 let pseudo = RequestPseudo {
3626 method: crate::http::effective_method(req),
3627 scheme: req.url.scheme.clone(),
3628 authority,
3629 path: req.url.path.clone(),
3630 };
3631
3632 let mut fields: Vec<(String, String)> = Vec::new();
3633 let mut have_ua = false;
3634 let mut have_accept = false;
3635 let mut have_accept_enc = false;
3636 let mut have_auth = false;
3637 for (k, v) in &req.headers {
3638 if is_connection_specific_header(k) || k.eq_ignore_ascii_case("host") {
3639 continue;
3640 }
3641 let lk = k.to_ascii_lowercase();
3642 if lk == "user-agent" {
3643 have_ua = true;
3644 }
3645 if lk == "accept" {
3646 have_accept = true;
3647 }
3648 if lk == "accept-encoding" {
3649 have_accept_enc = true;
3650 }
3651 if lk == "authorization" {
3652 have_auth = true;
3653 }
3654 fields.push((lk, v.clone()));
3655 }
3656 if !req.strict_headers {
3659 if !have_auth {
3660 if let Some(creds) = crate::http::effective_basic_auth(req) {
3661 fields.push(("authorization".to_string(), format!("Basic {creds}")));
3662 }
3663 }
3664 if !have_ua {
3665 fields.push((
3666 "user-agent".to_string(),
3667 concat!("rsurl/", env!("CARGO_PKG_VERSION")).to_string(),
3668 ));
3669 }
3670 if !have_accept {
3671 fields.push(("accept".to_string(), "*/*".to_string()));
3672 }
3673 if !have_accept_enc {
3674 fields.push(("accept-encoding".to_string(), "gzip, deflate".to_string()));
3678 }
3679 }
3680 if !req.body.is_empty() {
3681 fields.push(("content-length".to_string(), req.body.len().to_string()));
3682 }
3683 (pseudo, fields)
3684}
3685
3686struct RequestPseudo {
3688 method: String,
3689 scheme: String,
3690 authority: String,
3691 path: String,
3692}
3693
3694fn build_header_block(encoder: &mut Encoder, req: &Request) -> Vec<u8> {
3695 let mut out = Vec::new();
3696 let (pseudo, fields) = request_header_fields(req);
3697
3698 encoder.encode_header(&mut out, ":method", &pseudo.method);
3701 encoder.encode_header(&mut out, ":scheme", &pseudo.scheme);
3702 encoder.encode_header(&mut out, ":authority", &pseudo.authority);
3703 encoder.encode_header(&mut out, ":path", &pseudo.path);
3704
3705 for (k, v) in &fields {
3707 encoder.encode_header(&mut out, k, v);
3708 }
3709 out
3710}
3711
3712fn trace_request(req: &Request, trace: &mut dyn Write) {
3718 let (pseudo, fields) = request_header_fields(req);
3719 let _ = writeln!(trace, "> {} {} HTTP/2", pseudo.method, pseudo.path);
3720 let _ = writeln!(trace, "> Host: {}", pseudo.authority);
3721 for (k, v) in &fields {
3722 let _ = writeln!(trace, "> {k}: {v}");
3723 }
3724 let _ = writeln!(trace, "> ");
3725}
3726
3727fn is_connection_specific_header(name: &str) -> bool {
3728 matches!(
3730 name.to_ascii_lowercase().as_str(),
3731 "connection" | "proxy-connection" | "keep-alive" | "transfer-encoding" | "upgrade" | "te" )
3733}
3734
3735fn trace_request_labelled(req: &Request, id: u32, trace: &mut dyn Write) {
3738 let (pseudo, fields) = request_header_fields(req);
3739 let _ = writeln!(
3740 trace,
3741 "> [stream {id}] {} {} HTTP/2",
3742 pseudo.method, pseudo.path
3743 );
3744 let _ = writeln!(trace, "> [stream {id}] Host: {}", pseudo.authority);
3745 for (k, v) in &fields {
3746 let _ = writeln!(trace, "> [stream {id}] {k}: {v}");
3747 }
3748 let _ = writeln!(trace, "> [stream {id}] ");
3749}
3750
3751fn collect_results(results: Vec<Option<Result<Response>>>) -> Vec<Result<Response>> {
3755 results
3756 .into_iter()
3757 .map(|slot| {
3758 slot.unwrap_or_else(|| {
3759 Err(Error::BadResponse(
3760 "internal: multiplexed request produced no result".into(),
3761 ))
3762 })
3763 })
3764 .collect()
3765}
3766
3767fn clone_error(e: &Error) -> Error {
3773 match e {
3774 Error::InvalidUrl(s) => Error::InvalidUrl(s.clone()),
3775 Error::UnsupportedScheme(s) => Error::UnsupportedScheme(s.clone()),
3776 Error::Io(io_err) => Error::Io(io::Error::new(io_err.kind(), io_err.to_string())),
3777 Error::BadResponse(s) => Error::BadResponse(s.clone()),
3778 Error::UnexpectedEof => Error::UnexpectedEof,
3779 Error::H2NotNegotiated => Error::H2NotNegotiated,
3780 Error::Ssh(s) => Error::Ssh(s.clone()),
3781 Error::Decode(s) => Error::Decode(s.clone()),
3782 Error::Status { code, reason } => Error::Status {
3783 code: *code,
3784 reason: reason.clone(),
3785 },
3786 Error::Cancelled => Error::Cancelled,
3787 }
3788}
3789
3790pub fn send_multiplexed(reqs: Vec<Request>, trace: &mut dyn Write) -> Vec<Result<Response>> {
3818 if reqs.is_empty() {
3819 return Vec::new();
3820 }
3821
3822 let first = &reqs[0];
3824 let same_origin_https = first.url.scheme == "https"
3825 && reqs.iter().all(|r| {
3826 r.url.scheme == first.url.scheme
3827 && r.url.host == first.url.host
3828 && r.url.port == first.url.port
3829 });
3830 let all_eligible = reqs.iter().all(pool_eligible);
3831
3832 if !same_origin_https || !all_eligible {
3833 let _ = writeln!(
3836 trace,
3837 "* multiplexing preconditions not met (mixed origin / non-https / non-pool-eligible TLS); issuing requests sequentially"
3838 );
3839 return reqs.into_iter().map(|r| send(r, trace)).collect();
3840 }
3841
3842 let key = PoolKey::from_request(first);
3843
3844 let pooled = {
3850 let mut guard = global_pool().lock().unwrap_or_else(|e| e.into_inner());
3851 guard.checkout(&key)
3852 };
3853 if let Some(arc) = pooled {
3854 let mut conn_guard = arc.lock().unwrap_or_else(|e| e.into_inner());
3855 if conn_guard.is_usable() {
3856 let _ = writeln!(
3857 trace,
3858 "* Reusing existing connection from pool (multiplexed)"
3859 );
3860 let results = conn_guard.run_multiplexed(&reqs, trace);
3861 let clean = results.iter().all(Result::is_ok) && conn_guard.is_usable();
3864 drop(conn_guard);
3865 if clean {
3866 let mut guard = global_pool().lock().unwrap_or_else(|e| e.into_inner());
3867 guard.release(key, arc);
3868 let _ = writeln!(trace, "* Connection kept alive (pooled)");
3869 } else {
3870 let _ = writeln!(trace, "* Connection closed");
3871 }
3872 return results;
3873 }
3874 drop(conn_guard);
3876 let _ = writeln!(
3877 trace,
3878 "* Pooled connection unusable (connection closed); reconnecting"
3879 );
3880 }
3881
3882 let (mut fresh, _cancel_guard) = match dial_h2(first, trace) {
3884 Ok(c) => c,
3885 Err(e) => {
3886 let _ = writeln!(
3890 trace,
3891 "* HTTP/2 connection for multiplexing failed ({e}); issuing requests sequentially"
3892 );
3893 return reqs.into_iter().map(|r| send(r, trace)).collect();
3894 }
3895 };
3896 let results = fresh.run_multiplexed(&reqs, trace);
3897 let clean = results.iter().all(Result::is_ok) && fresh.is_usable();
3898 if clean {
3899 let arc = Arc::new(Mutex::new(fresh));
3900 let mut guard = global_pool().lock().unwrap_or_else(|e| e.into_inner());
3901 guard.release(key, arc);
3902 let _ = writeln!(trace, "* Connection kept alive (pooled)");
3903 } else {
3904 let _ = writeln!(trace, "* Connection closed");
3905 }
3906 results
3907}
3908
3909#[cfg(test)]
3914mod tests {
3915 use super::*;
3916 use std::io::Cursor;
3917
3918 #[test]
3919 fn int_encode_small() {
3920 assert_eq!(encode_int(10, 5), vec![10]);
3922 }
3923
3924 #[test]
3925 fn int_encode_large() {
3926 assert_eq!(encode_int(1337, 5), vec![0x1f, 0x9a, 0x0a]);
3928 }
3929
3930 #[test]
3931 fn int_encode_eight_bit() {
3932 assert_eq!(encode_int(42, 8), vec![42]);
3934 }
3935
3936 #[test]
3937 fn int_decode_round_trips() {
3938 for &(v, p) in &[
3939 (0u64, 5),
3940 (10, 5),
3941 (30, 5),
3942 (31, 5),
3943 (1337, 5),
3944 (1, 8),
3945 (255, 8),
3946 ] {
3947 let enc = encode_int(v, p);
3948 let (dec, n) = decode_int(&enc, p).unwrap();
3949 assert_eq!(dec, v, "value {v} with {p}-bit prefix");
3950 assert_eq!(n, enc.len());
3951 }
3952 }
3953
3954 #[test]
3955 fn int_decode_truncated_errors() {
3956 assert!(decode_int(&[0x1f], 5).is_err());
3958 assert!(decode_int(&[0x1f, 0x80], 5).is_err());
3959 }
3960
3961 #[test]
3962 fn static_table_method_get() {
3963 assert_eq!(static_full_index(":method", "GET"), Some(2));
3965 }
3966
3967 #[test]
3968 fn static_table_method_post() {
3969 assert_eq!(static_full_index(":method", "POST"), Some(3));
3970 }
3971
3972 #[test]
3973 fn static_table_name_only() {
3974 assert_eq!(static_name_index(":status"), Some(8));
3975 assert_eq!(static_name_index("user-agent"), Some(58));
3976 assert_eq!(static_name_index("does-not-exist"), None);
3977 }
3978
3979 #[test]
3980 fn static_table_length() {
3981 assert_eq!(STATIC_TABLE.len(), 61);
3982 }
3983
3984 #[test]
3985 fn frame_round_trip_empty_settings() {
3986 let f = Frame {
3987 typ: F_SETTINGS,
3988 flags: 0,
3989 stream_id: 0,
3990 payload: Vec::new(),
3991 };
3992 let mut buf = Vec::new();
3993 write_frame(&mut buf, &f).unwrap();
3994 assert_eq!(buf.len(), 9);
3995 let mut cur = Cursor::new(buf);
3996 let g = read_frame(&mut cur).unwrap();
3997 assert_eq!(g, f);
3998 }
3999
4000 #[test]
4001 fn frame_round_trip_headers_with_payload() {
4002 let f = Frame {
4003 typ: F_HEADERS,
4004 flags: FLAG_END_STREAM | FLAG_END_HEADERS,
4005 stream_id: 1,
4006 payload: vec![
4007 0x82, 0x86, 0x84, 0x41, 0x88, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab,
4008 0x90, 0xf4, 0xff,
4009 ],
4010 };
4011 let mut buf = Vec::new();
4012 write_frame(&mut buf, &f).unwrap();
4013 let mut cur = Cursor::new(buf);
4014 let g = read_frame(&mut cur).unwrap();
4015 assert_eq!(g, f);
4016 assert_eq!(g.flags, 0x05);
4017 }
4018
4019 #[test]
4020 fn frame_stream_id_high_bit_masked_on_read() {
4021 let buf = vec![0, 0, 0, F_DATA, 0, 0x80, 0, 0, 1];
4023 let mut cur = Cursor::new(buf);
4024 let f = read_frame(&mut cur).unwrap();
4025 assert_eq!(f.stream_id, 1);
4026 }
4027
4028 #[test]
4029 fn hpack_encode_indexed_method() {
4030 let mut enc = Encoder::new();
4033 let mut out = Vec::new();
4034 enc.encode_header(&mut out, ":method", "GET");
4035 assert_eq!(out, vec![0x82]);
4036 assert!(enc.dyn_table.is_empty());
4037 }
4038
4039 #[test]
4040 fn hpack_encode_literal_with_indexed_name() {
4041 let mut enc = Encoder::new();
4047 let mut out = Vec::new();
4048 enc.encode_header(&mut out, ":path", "/foo");
4049 assert_eq!(out[0], 0x44);
4050 let mut dec = Decoder::new();
4051 let got = dec.decode_block(&out).unwrap();
4052 assert_eq!(got, vec![(":path".into(), "/foo".into())]);
4053 assert_eq!(enc.dyn_table.len(), 1);
4054 assert_eq!(enc.dyn_table[0], (":path".to_string(), "/foo".to_string()));
4055 }
4056
4057 #[test]
4058 fn hpack_encode_literal_full() {
4059 let mut enc = Encoder::new();
4062 let mut out = Vec::new();
4063 enc.encode_header(&mut out, "x-custom", "yes");
4064 assert_eq!(out[0], 0x40);
4065 let mut dec = Decoder::new();
4066 let got = dec.decode_block(&out).unwrap();
4067 assert_eq!(got, vec![("x-custom".into(), "yes".into())]);
4068 assert_eq!(enc.dyn_table[0], ("x-custom".into(), "yes".into()));
4069 }
4070
4071 #[test]
4072 fn hpack_decode_round_trip_pseudo_headers() {
4073 let mut enc = Encoder::new();
4074 let mut block = Vec::new();
4075 enc.encode_header(&mut block, ":method", "GET");
4076 enc.encode_header(&mut block, ":scheme", "https");
4077 enc.encode_header(&mut block, ":authority", "example.com");
4078 enc.encode_header(&mut block, ":path", "/");
4079 let mut dec = Decoder::new();
4080 let got = dec.decode_block(&block).unwrap();
4081 assert_eq!(got.len(), 4);
4082 assert_eq!(got[0], (":method".into(), "GET".into()));
4083 assert_eq!(got[1], (":scheme".into(), "https".into()));
4084 assert_eq!(got[2], (":authority".into(), "example.com".into()));
4085 assert_eq!(got[3], (":path".into(), "/".into()));
4086 }
4087
4088 #[test]
4089 fn hpack_decode_indexed_static() {
4090 let mut dec = Decoder::new();
4092 let got = dec.decode_block(&[0x82]).unwrap();
4093 assert_eq!(got, vec![(":method".into(), "GET".into())]);
4094 }
4095
4096 #[test]
4097 fn hpack_decode_literal_with_incremental_indexing() {
4098 let buf: Vec<u8> = vec![
4101 0x40, 0x0a, b'c', b'u', b's', b't', b'o', b'm', b'-', b'k', b'e', b'y', 0x0d, b'c',
4102 b'u', b's', b't', b'o', b'm', b'-', b'h', b'e', b'a', b'd', b'e', b'r',
4103 ];
4104 let mut dec = Decoder::new();
4105 let got = dec.decode_block(&buf).unwrap();
4106 assert_eq!(got, vec![("custom-key".into(), "custom-header".into())]);
4107 assert_eq!(dec.dyn_table.len(), 1);
4109 }
4110
4111 fn raw_literal_block(name: &[u8], value: &[u8]) -> Vec<u8> {
4115 let mut buf = vec![0x00u8];
4116 buf.push(name.len() as u8); buf.extend_from_slice(name);
4118 buf.push(value.len() as u8);
4119 buf.extend_from_slice(value);
4120 buf
4121 }
4122
4123 #[test]
4124 fn hpack_decode_rejects_crlf_in_value() {
4125 let block = raw_literal_block(b"x-h", b"evil\r\nset-cookie: x=1");
4127 let mut dec = Decoder::new();
4128 let err = dec.decode_block(&block).unwrap_err();
4129 assert!(matches!(err, Error::BadResponse(_)), "got {err:?}");
4130 }
4131
4132 #[test]
4133 fn hpack_decode_rejects_lf_in_value() {
4134 let block = raw_literal_block(b"x-h", b"a\nb");
4135 let mut dec = Decoder::new();
4136 assert!(matches!(
4137 dec.decode_block(&block).unwrap_err(),
4138 Error::BadResponse(_)
4139 ));
4140 }
4141
4142 #[test]
4143 fn hpack_decode_rejects_nul_in_value() {
4144 let block = raw_literal_block(b"x-h", b"a\x00b");
4145 let mut dec = Decoder::new();
4146 assert!(matches!(
4147 dec.decode_block(&block).unwrap_err(),
4148 Error::BadResponse(_)
4149 ));
4150 }
4151
4152 #[test]
4153 fn hpack_decode_rejects_uppercase_name() {
4154 let block = raw_literal_block(b"X-Bad", b"ok");
4155 let mut dec = Decoder::new();
4156 assert!(matches!(
4157 dec.decode_block(&block).unwrap_err(),
4158 Error::BadResponse(_)
4159 ));
4160 }
4161
4162 #[test]
4163 fn hpack_decode_rejects_empty_name() {
4164 let block = raw_literal_block(b"", b"ok");
4165 let mut dec = Decoder::new();
4166 assert!(matches!(
4167 dec.decode_block(&block).unwrap_err(),
4168 Error::BadResponse(_)
4169 ));
4170 }
4171
4172 #[test]
4173 fn hpack_decode_accepts_normal_header_and_pseudo() {
4174 let mut block = raw_literal_block(b"content-type", b"text/html; charset=utf-8");
4176 block.extend(raw_literal_block(b":status", b"200"));
4177 let mut dec = Decoder::new();
4178 let got = dec.decode_block(&block).unwrap();
4179 assert_eq!(
4180 got[0],
4181 ("content-type".into(), "text/html; charset=utf-8".into())
4182 );
4183 assert_eq!(got[1], (":status".into(), "200".into()));
4184 let tabbed = raw_literal_block(b"x-h", b"a\tb");
4186 let mut dec2 = Decoder::new();
4187 assert!(dec2.decode_block(&tabbed).is_ok());
4188 }
4189
4190 #[test]
4191 fn huffman_decode_c4_1() {
4192 let coded = [
4194 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
4195 ];
4196 let out = huffman_decode(&coded).unwrap();
4197 assert_eq!(out, b"www.example.com");
4198 }
4199
4200 #[test]
4201 fn huffman_decode_c4_2() {
4202 let coded = [0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf];
4204 let out = huffman_decode(&coded).unwrap();
4205 assert_eq!(out, b"no-cache");
4206 }
4207
4208 #[test]
4209 fn huffman_decode_c4_3() {
4210 let coded = [0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f];
4212 let out = huffman_decode(&coded).unwrap();
4213 assert_eq!(out, b"custom-key");
4214 }
4215
4216 #[test]
4217 fn huffman_decode_rejects_short_padding() {
4218 assert!(huffman_decode(&[0x00]).is_err());
4221 }
4222
4223 #[test]
4228 fn huffman_encode_padding_bits() {
4229 let out = huffman_encode(b"a");
4233 assert_eq!(out, vec![0x1f]);
4234 }
4235
4236 #[test]
4237 fn huffman_encode_appendix_c_www_example_com() {
4238 let out = huffman_encode(b"www.example.com");
4240 assert_eq!(
4241 out,
4242 vec![0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,]
4243 );
4244 }
4245
4246 #[test]
4247 fn huffman_encode_appendix_c_no_cache() {
4248 let out = huffman_encode(b"no-cache");
4250 assert_eq!(out, vec![0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf]);
4251 }
4252
4253 #[test]
4254 fn huffman_encode_appendix_c_custom_key() {
4255 let out = huffman_encode(b"custom-key");
4257 assert_eq!(out, vec![0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f]);
4258 }
4259
4260 #[test]
4261 fn huffman_encode_appendix_c_custom_value() {
4262 let out = huffman_encode(b"custom-value");
4264 assert_eq!(
4265 out,
4266 vec![0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf]
4267 );
4268 }
4269
4270 #[test]
4271 fn huffman_encode_round_trips_through_decoder() {
4272 for s in &[
4275 "",
4276 "a",
4277 "ab",
4278 "abc",
4279 "Hello, world!",
4280 "the quick brown fox jumps",
4281 "/foo/bar/baz",
4282 ] {
4283 let bytes = s.as_bytes();
4284 if bytes.is_empty() {
4285 let enc = huffman_encode(bytes);
4288 assert!(enc.is_empty());
4289 continue;
4290 }
4291 let enc = huffman_encode(bytes);
4292 let dec = huffman_decode(&enc).unwrap();
4293 assert_eq!(dec, bytes, "round-trip mismatch for {s:?}");
4294 }
4295 }
4296
4297 #[test]
4298 fn encode_literal_chooses_huffman_when_shorter() {
4299 let mut out = Vec::new();
4302 let s: String = "a".repeat(100);
4303 encode_literal_string(&mut out, &s);
4304 assert_eq!(out[0] & 0x80, 0x80, "Huffman bit should be set");
4305 }
4306
4307 #[test]
4308 fn encode_literal_chooses_raw_when_huffman_longer() {
4309 let mut out = Vec::new();
4312 let bytes: Vec<u8> = vec![0xff; 100];
4318 let huff = huffman_encode(&bytes);
4324 assert!(
4325 huff.len() > bytes.len(),
4326 "0xff Huffman should be longer than raw"
4327 );
4328 let s: String = "|".repeat(50);
4332 out.clear();
4333 encode_literal_string(&mut out, &s);
4334 assert_eq!(out[0] & 0x80, 0x00, "Huffman bit should be cleared");
4335 assert_eq!(out[0] as usize & 0x7f, 50);
4336 assert_eq!(&out[1..], s.as_bytes());
4337 }
4338
4339 #[test]
4344 fn encoder_inserts_into_dyn_table_on_incremental_indexing() {
4345 let mut enc = Encoder::new();
4346 let mut out = Vec::new();
4347 enc.encode_header(&mut out, "x-custom", "value1");
4348 assert_eq!(enc.dyn_table.len(), 1);
4349 assert_eq!(
4350 enc.dyn_table[0],
4351 ("x-custom".to_string(), "value1".to_string())
4352 );
4353 assert_eq!(enc.dyn_table_size, "x-custom".len() + "value1".len() + 32);
4354 }
4355
4356 #[test]
4357 fn encoder_evicts_to_fit_max_size() {
4358 let mut enc = Encoder::new();
4362 enc.max_dyn_table_size = 64;
4363 let mut out = Vec::new();
4364 enc.encode_header(&mut out, "n1aa", "v1aa");
4365 enc.encode_header(&mut out, "n2aa", "v2aa");
4366 assert_eq!(enc.dyn_table.len(), 1, "only the newest should remain");
4367 assert_eq!(enc.dyn_table[0], ("n2aa".to_string(), "v2aa".to_string()));
4368 assert_eq!(enc.dyn_table_size, 40);
4369 }
4370
4371 #[test]
4372 fn encoder_emits_size_update_signal_on_next_encode_after_setting_change() {
4373 let mut enc = Encoder::new();
4374 enc.set_peer_max_table_size(1024);
4375 let mut out = Vec::new();
4376 enc.encode_header(&mut out, ":method", "GET");
4377 assert_eq!(out, vec![0x3f, 0xe1, 0x07, 0x82]);
4381 out.clear();
4383 enc.encode_header(&mut out, ":method", "GET");
4384 assert_eq!(out, vec![0x82]);
4385 }
4386
4387 #[test]
4388 fn encoder_uses_dynamic_index_for_repeat() {
4389 let mut enc = Encoder::new();
4390 let mut out = Vec::new();
4391 enc.encode_header(&mut out, "x", "y");
4392 out.clear();
4393 enc.encode_header(&mut out, "x", "y");
4394 assert_eq!(out, vec![0xbe]);
4396 }
4397
4398 #[test]
4399 fn encoder_uses_indexed_name_from_dyn_table() {
4400 let mut enc = Encoder::new();
4401 let mut out = Vec::new();
4402 enc.encode_header(&mut out, "x-foo", "v1");
4403 out.clear();
4405 enc.encode_header(&mut out, "x-foo", "v2");
4406 assert_eq!(out[0], 0x7e);
4408 assert_eq!(enc.dyn_table.len(), 2);
4410 assert_eq!(enc.dyn_table[0].1, "v2");
4411 assert_eq!(enc.dyn_table[1].1, "v1");
4412 }
4413
4414 #[test]
4415 fn encode_decode_round_trip() {
4416 let mut enc = Encoder::new();
4420 let mut dec = Decoder::new();
4421 let inputs: Vec<(&str, &str)> = vec![
4422 (":method", "GET"),
4423 (":scheme", "https"),
4424 (":authority", "example.com"),
4425 (":path", "/foo"),
4426 ("user-agent", "rsurl/test"),
4427 ("accept", "*/*"),
4428 ("x-custom", "hello world"),
4429 ("user-agent", "rsurl/test"), ("x-custom", "different"), ];
4432 let mut buf = Vec::new();
4433 for (n, v) in &inputs {
4434 enc.encode_header(&mut buf, n, v);
4435 }
4436 let got = dec.decode_block(&buf).unwrap();
4437 let expected: Vec<(String, String)> = inputs
4438 .into_iter()
4439 .map(|(n, v)| (n.to_string(), v.to_string()))
4440 .collect();
4441 assert_eq!(got, expected);
4442 }
4443
4444 #[test]
4445 fn encoder_size_update_evicts_oversize_entries_immediately() {
4446 let mut enc = Encoder::new();
4450 let mut out = Vec::new();
4451 enc.encode_header(&mut out, "n1aa", "v1aa"); enc.encode_header(&mut out, "n2aa", "v2aa"); assert_eq!(enc.dyn_table.len(), 2);
4454 enc.set_peer_max_table_size(50);
4455 assert_eq!(enc.dyn_table.len(), 1);
4456 assert_eq!(enc.dyn_table[0].0, "n2aa");
4457 }
4458
4459 #[test]
4460 fn hpack_decode_huffman_literal_value() {
4461 let buf = vec![
4476 0x44, 0x8c, 0x60, 0xd4, 0x85, 0x31, 0x68, 0xdf, 0x1c, 0x6f, 0xa2, 0xa6, 0xfd, 0x95,
4477 0xb6, 0x88,
4478 ];
4479 let _ = Decoder::new().decode_block(&buf);
4483 }
4484
4485 #[test]
4486 fn build_header_block_includes_pseudo() {
4487 let req = Request::new("GET", "https://example.com/foo").unwrap();
4488 let mut enc = Encoder::new();
4489 let block = build_header_block(&mut enc, &req);
4490 let mut dec = Decoder::new();
4491 let headers = dec.decode_block(&block).unwrap();
4492 let kv: Vec<(&str, &str)> = headers
4493 .iter()
4494 .map(|(k, v)| (k.as_str(), v.as_str()))
4495 .collect();
4496 assert!(kv.contains(&(":method", "GET")));
4497 assert!(kv.contains(&(":scheme", "https")));
4498 assert!(kv.contains(&(":authority", "example.com")));
4499 assert!(kv.contains(&(":path", "/foo")));
4500 assert!(kv.iter().any(|(k, _)| *k == "user-agent"));
4501 assert!(kv.iter().any(|(k, _)| *k == "accept"));
4502 }
4503
4504 #[test]
4505 fn build_header_block_strips_banned_headers() {
4506 let req = Request::new("GET", "https://example.com/")
4507 .unwrap()
4508 .header("Connection", "close")
4509 .header("Host", "evil.example")
4510 .header("X-Allowed", "yes");
4511 let mut enc = Encoder::new();
4512 let block = build_header_block(&mut enc, &req);
4513 let mut dec = Decoder::new();
4514 let headers = dec.decode_block(&block).unwrap();
4515 let names: Vec<&str> = headers.iter().map(|(k, _)| k.as_str()).collect();
4516 assert!(!names.contains(&"connection"));
4517 assert!(!names.contains(&"host"));
4518 assert!(names.contains(&"x-allowed"));
4519 }
4520
4521 #[test]
4522 fn build_header_block_authority_includes_nonstandard_port() {
4523 let req = Request::new("GET", "https://example.com:8443/").unwrap();
4524 let mut enc = Encoder::new();
4525 let block = build_header_block(&mut enc, &req);
4526 let mut dec = Decoder::new();
4527 let headers = dec.decode_block(&block).unwrap();
4528 let auth = headers.iter().find(|(k, _)| k == ":authority").unwrap();
4529 assert_eq!(auth.1, "example.com:8443");
4530 }
4531
4532 #[test]
4533 fn decoder_dynamic_table_size_update_caps_to_4096() {
4534 let mut dec = Decoder::new();
4536 dec.decode_block(&[0x20]).unwrap();
4537 assert_eq!(dec.dyn_table_cap, 0);
4538 }
4539
4540 #[test]
4541 fn decoder_rejects_oversize_index() {
4542 let mut dec = Decoder::new();
4543 let err = dec.decode_block(&[0xff, 0x01]).unwrap_err();
4545 match err {
4546 Error::BadResponse(_) => {}
4547 other => panic!("expected BadResponse, got {other:?}"),
4548 }
4549 }
4550
4551 fn settings_payload(entries: &[(u16, u32)]) -> Vec<u8> {
4557 let mut out = Vec::with_capacity(entries.len() * 6);
4558 for (id, val) in entries {
4559 out.extend_from_slice(&id.to_be_bytes());
4560 out.extend_from_slice(&val.to_be_bytes());
4561 }
4562 out
4563 }
4564
4565 #[test]
4566 fn peer_settings_defaults_match_rfc() {
4567 let p = PeerSettings::default();
4568 assert_eq!(p.header_table_size, 4096);
4569 assert!(p.enable_push);
4570 assert_eq!(p.max_concurrent_streams, u32::MAX);
4571 assert_eq!(p.initial_window_size, 65_535);
4572 assert_eq!(p.max_frame_size, 16_384);
4573 assert_eq!(p.max_header_list_size, u32::MAX);
4574 }
4575
4576 #[test]
4577 fn peer_settings_apply_updates_known_identifiers() {
4578 let mut p = PeerSettings::default();
4579 let payload = settings_payload(&[
4580 (S_HEADER_TABLE_SIZE, 8192),
4581 (S_INITIAL_WINDOW_SIZE, 131_072),
4582 (S_MAX_FRAME_SIZE, 32_768),
4583 ]);
4584 p.apply_settings_payload(&payload).unwrap();
4585 assert_eq!(p.header_table_size, 8192);
4586 assert_eq!(p.initial_window_size, 131_072);
4587 assert_eq!(p.max_frame_size, 32_768);
4588 assert!(p.enable_push);
4590 assert_eq!(p.max_concurrent_streams, u32::MAX);
4591 assert_eq!(p.max_header_list_size, u32::MAX);
4592 }
4593
4594 #[test]
4595 fn peer_settings_ignores_unknown_identifier() {
4596 let mut p = PeerSettings::default();
4597 let before = p.clone();
4598 let payload = settings_payload(&[(0xFFFF, 42)]);
4599 p.apply_settings_payload(&payload).unwrap();
4600 assert_eq!(p, before);
4601 }
4602
4603 #[test]
4604 fn peer_settings_rejects_bad_enable_push() {
4605 let mut p = PeerSettings::default();
4606 let payload = settings_payload(&[(S_ENABLE_PUSH, 2)]);
4607 let err = p.apply_settings_payload(&payload).unwrap_err();
4608 match err {
4609 Error::BadResponse(_) => {}
4610 other => panic!("expected BadResponse, got {other:?}"),
4611 }
4612 }
4613
4614 #[test]
4615 fn peer_settings_rejects_oversize_window() {
4616 let mut p = PeerSettings::default();
4617 let payload = settings_payload(&[(S_INITIAL_WINDOW_SIZE, 0x8000_0000)]);
4619 let err = p.apply_settings_payload(&payload).unwrap_err();
4620 match err {
4621 Error::BadResponse(_) => {}
4622 other => panic!("expected BadResponse, got {other:?}"),
4623 }
4624 }
4625
4626 #[test]
4627 fn peer_settings_rejects_undersize_max_frame() {
4628 let mut p = PeerSettings::default();
4629 let payload = settings_payload(&[(S_MAX_FRAME_SIZE, 16_383)]);
4630 let err = p.apply_settings_payload(&payload).unwrap_err();
4631 match err {
4632 Error::BadResponse(_) => {}
4633 other => panic!("expected BadResponse, got {other:?}"),
4634 }
4635 }
4636
4637 #[test]
4638 fn peer_settings_rejects_truncated_payload() {
4639 let mut p = PeerSettings::default();
4640 let payload = vec![0u8; 5];
4641 let err = p.apply_settings_payload(&payload).unwrap_err();
4642 match err {
4643 Error::BadResponse(_) => {}
4644 other => panic!("expected BadResponse, got {other:?}"),
4645 }
4646 }
4647
4648 #[test]
4649 fn peer_settings_enable_push_zero_disables() {
4650 let mut p = PeerSettings::default();
4652 p.apply_settings_payload(&settings_payload(&[(S_ENABLE_PUSH, 0)]))
4653 .unwrap();
4654 assert!(!p.enable_push);
4655 p.apply_settings_payload(&settings_payload(&[(S_ENABLE_PUSH, 1)]))
4656 .unwrap();
4657 assert!(p.enable_push);
4658 }
4659
4660 #[test]
4661 fn peer_settings_max_frame_size_boundaries() {
4662 let mut p = PeerSettings::default();
4664 p.apply_settings_payload(&settings_payload(&[(S_MAX_FRAME_SIZE, 16_384)]))
4665 .unwrap();
4666 assert_eq!(p.max_frame_size, 16_384);
4667 p.apply_settings_payload(&settings_payload(&[(S_MAX_FRAME_SIZE, 16_777_215)]))
4668 .unwrap();
4669 assert_eq!(p.max_frame_size, 16_777_215);
4670 let err = p
4672 .apply_settings_payload(&settings_payload(&[(S_MAX_FRAME_SIZE, 16_777_216)]))
4673 .unwrap_err();
4674 assert!(matches!(err, Error::BadResponse(_)));
4675 }
4676
4677 #[test]
4682 fn send_window_defaults_match_rfc() {
4683 let c = ConnSendWindow::new();
4685 assert_eq!(c.available, 65_535);
4686 let s = StreamSendWindow::new(65_535);
4687 assert_eq!(s.available, 65_535);
4688 assert_eq!(s.initial_peer_window, 65_535);
4689 }
4690
4691 #[test]
4692 fn send_window_decrements_after_data() {
4693 let mut c = ConnSendWindow::new();
4695 let mut s = StreamSendWindow::new(65_535);
4696 c.consume(1000);
4697 s.consume(1000);
4698 assert_eq!(c.available, 64_535);
4699 assert_eq!(s.available, 64_535);
4700 c.consume(64_535);
4701 s.consume(64_535);
4702 assert_eq!(c.available, 0);
4703 assert_eq!(s.available, 0);
4704 }
4705
4706 #[test]
4707 fn window_update_zero_increment_is_error() {
4708 let zero_payload = [0u8; 4];
4711 let inc = parse_window_update(&zero_payload).unwrap();
4712 assert_eq!(inc, 0);
4713 let mut c = ConnSendWindow::new();
4714 assert!(matches!(
4715 c.apply_window_update(inc),
4716 Err(Error::BadResponse(_))
4717 ));
4718 let mut s = StreamSendWindow::new(65_535);
4719 assert!(matches!(
4720 s.apply_window_update(inc),
4721 Err(Error::BadResponse(_))
4722 ));
4723 }
4724
4725 #[test]
4726 fn window_update_overflow_is_error() {
4727 let mut c = ConnSendWindow::new();
4730 c.available = WINDOW_MAX;
4731 assert!(matches!(
4732 c.apply_window_update(1),
4733 Err(Error::BadResponse(_))
4734 ));
4735 let mut s = StreamSendWindow::new(65_535);
4736 s.available = WINDOW_MAX;
4737 assert!(matches!(
4738 s.apply_window_update(1),
4739 Err(Error::BadResponse(_))
4740 ));
4741 }
4742
4743 #[test]
4744 fn window_update_high_bit_ignored_on_parse() {
4745 let payload = [0x80, 0x00, 0x00, 0x01];
4748 let inc = parse_window_update(&payload).unwrap();
4749 assert_eq!(inc, 1);
4750 }
4751
4752 #[test]
4753 fn window_update_wrong_length_is_error() {
4754 assert!(matches!(
4756 parse_window_update(&[0u8; 3]),
4757 Err(Error::BadResponse(_))
4758 ));
4759 assert!(matches!(
4760 parse_window_update(&[0u8; 5]),
4761 Err(Error::BadResponse(_))
4762 ));
4763 }
4764
4765 #[test]
4766 fn initial_window_size_delta_adjusts_stream_send_window() {
4767 let mut s = StreamSendWindow::new(65_535);
4770 s.apply_initial_window_change(131_072).unwrap();
4771 assert_eq!(s.available, 65_535 + (131_072 - 65_535));
4772 assert_eq!(s.initial_peer_window, 131_072);
4773 s.apply_initial_window_change(0).unwrap();
4776 assert_eq!(s.available, 0);
4778 assert_eq!(s.initial_peer_window, 0);
4779 }
4780
4781 #[test]
4782 fn initial_window_size_delta_overflow_is_error() {
4783 let mut s = StreamSendWindow::new(65_535);
4787 s.available = WINDOW_MAX;
4788 let err = s.apply_initial_window_change(65_536).unwrap_err();
4789 assert!(matches!(err, Error::BadResponse(_)));
4790 }
4791
4792 #[test]
4793 fn initial_window_size_delta_allows_negative_window() {
4794 let mut s = StreamSendWindow::new(65_535);
4797 s.available = 100;
4798 s.apply_initial_window_change(0).unwrap();
4799 assert_eq!(s.available, -65_435);
4801 }
4802
4803 #[test]
4804 fn recv_window_new_sets_available_and_initial() {
4805 let c = ConnRecvWindow::new(OUR_INITIAL_WINDOW);
4806 assert_eq!(c.available, OUR_INITIAL_WINDOW);
4807 assert_eq!(c.initial, OUR_INITIAL_WINDOW);
4808 let s = StreamRecvWindow::new(DEFAULT_RECV_WINDOW as i64);
4809 assert_eq!(s.available, DEFAULT_RECV_WINDOW as i64);
4810 assert_eq!(s.initial, DEFAULT_RECV_WINDOW as i64);
4811 }
4812
4813 #[test]
4814 fn recv_window_no_replenish_above_half() {
4815 let mut c = ConnRecvWindow::new(OUR_INITIAL_WINDOW);
4818 c.consume(1000);
4819 assert!(c.replenish().is_none());
4820 assert_eq!(c.available, OUR_INITIAL_WINDOW - 1000);
4821
4822 let mut s = StreamRecvWindow::new(OUR_INITIAL_WINDOW);
4823 s.consume(1000);
4824 assert!(s.replenish(1).is_none());
4825 assert_eq!(s.available, OUR_INITIAL_WINDOW - 1000);
4826 }
4827
4828 #[test]
4829 fn recv_window_replenishes_when_below_half() {
4830 let mut c = ConnRecvWindow::new(OUR_INITIAL_WINDOW);
4834 c.consume(20_000);
4835 c.consume(20_000);
4836 assert_eq!(c.available, 25_535);
4837 let f = c.replenish().expect("conn window expected replenish");
4838 assert_eq!(f.typ, F_WINDOW_UPDATE);
4839 assert_eq!(f.stream_id, 0);
4840 let inc = parse_window_update(&f.payload).unwrap();
4841 assert_eq!(inc, (OUR_INITIAL_WINDOW - 25_535) as u32);
4842 assert_eq!(c.available, OUR_INITIAL_WINDOW);
4843 assert!(c.replenish().is_none());
4845
4846 let mut s = StreamRecvWindow::new(OUR_INITIAL_WINDOW);
4847 s.consume(40_000);
4848 let f = s.replenish(7).expect("stream window expected replenish");
4849 assert_eq!(f.typ, F_WINDOW_UPDATE);
4850 assert_eq!(f.stream_id, 7);
4851 let inc = parse_window_update(&f.payload).unwrap();
4852 assert_eq!(inc, 40_000);
4853 assert_eq!(s.available, OUR_INITIAL_WINDOW);
4854 }
4855
4856 #[test]
4857 fn new_advertises_recv_window_and_bumps_conn_window() {
4858 let recv = 8 * 1024 * 1024u32;
4861 let conn = Connection::new(FakeTls::new(), recv).unwrap();
4862 let out = conn.tls.wire_out.clone();
4863 assert!(out.starts_with(PREFACE), "client preface comes first");
4864 let mut cur = Cursor::new(out[PREFACE.len()..].to_vec());
4865
4866 let settings = read_frame(&mut cur).unwrap();
4868 assert_eq!(settings.typ, F_SETTINGS);
4869 let iws = settings
4870 .payload
4871 .chunks_exact(6)
4872 .find(|c| u16::from_be_bytes([c[0], c[1]]) == S_INITIAL_WINDOW_SIZE)
4873 .map(|c| u32::from_be_bytes([c[2], c[3], c[4], c[5]]));
4874 assert_eq!(iws, Some(recv));
4875
4876 let wu = read_frame(&mut cur).unwrap();
4878 assert_eq!(wu.typ, F_WINDOW_UPDATE);
4879 assert_eq!(wu.stream_id, 0);
4880 assert_eq!(
4881 parse_window_update(&wu.payload).unwrap(),
4882 recv - OUR_INITIAL_WINDOW as u32
4883 );
4884
4885 assert_eq!(conn.conn_recv_window.available, recv as i64);
4887 assert_eq!(conn.our_recv_window, recv as i64);
4888 }
4889
4890 #[test]
4891 fn new_at_rfc_default_emits_no_conn_window_update() {
4892 let conn = Connection::new(FakeTls::new(), OUR_INITIAL_WINDOW as u32).unwrap();
4895 let mut cur = Cursor::new(conn.tls.wire_out[PREFACE.len()..].to_vec());
4896 assert_eq!(read_frame(&mut cur).unwrap().typ, F_SETTINGS);
4897 assert!(read_frame(&mut cur).is_err(), "no frame after SETTINGS");
4898 }
4899
4900 #[test]
4901 fn window_update_frame_payload_shape() {
4902 let f = window_update_frame(7, 0x0102_0304);
4905 assert_eq!(f.typ, F_WINDOW_UPDATE);
4906 assert_eq!(f.flags, 0);
4907 assert_eq!(f.stream_id, 7);
4908 assert_eq!(f.payload, vec![0x01, 0x02, 0x03, 0x04]);
4909 }
4910
4911 #[test]
4916 fn fragment_header_block_into_continuation() {
4917 let max: usize = 16_384;
4922 let payload_len = max * 2 + 7;
4923 let block: Vec<u8> = (0..payload_len).map(|i| (i & 0xff) as u8).collect();
4924
4925 let frames = fragment_header_block(1, &block, max, false);
4926 assert_eq!(frames.len(), 3, "expected HEADERS + 2 CONTINUATION");
4927
4928 assert_eq!(frames[0].typ, F_HEADERS);
4930 assert_eq!(frames[0].stream_id, 1);
4931 assert_eq!(frames[0].payload.len(), max);
4932 assert_eq!(frames[0].flags & FLAG_END_HEADERS, 0);
4933 assert_eq!(frames[0].flags & FLAG_END_STREAM, 0);
4934
4935 assert_eq!(frames[1].typ, F_CONTINUATION);
4937 assert_eq!(frames[1].stream_id, 1);
4938 assert_eq!(frames[1].payload.len(), max);
4939 assert_eq!(frames[1].flags, 0);
4940
4941 assert_eq!(frames[2].typ, F_CONTINUATION);
4943 assert_eq!(frames[2].stream_id, 1);
4944 assert_eq!(frames[2].payload.len(), 7);
4945 assert_eq!(frames[2].flags, FLAG_END_HEADERS);
4946
4947 let mut reassembled = Vec::with_capacity(payload_len);
4949 for f in &frames {
4950 reassembled.extend_from_slice(&f.payload);
4951 }
4952 assert_eq!(reassembled, block);
4953
4954 let frames = fragment_header_block(1, &block, max, true);
4957 assert_eq!(frames[0].flags & FLAG_END_STREAM, FLAG_END_STREAM);
4958 assert_eq!(frames[2].flags & FLAG_END_STREAM, 0);
4959 assert_eq!(frames[2].flags & FLAG_END_HEADERS, FLAG_END_HEADERS);
4960 }
4961
4962 #[test]
4963 fn fragment_header_block_exact_fit() {
4964 let max: usize = 16_384;
4967 let block: Vec<u8> = vec![0xab; max];
4968
4969 let frames = fragment_header_block(1, &block, max, false);
4971 assert_eq!(frames.len(), 1);
4972 assert_eq!(frames[0].typ, F_HEADERS);
4973 assert_eq!(frames[0].stream_id, 1);
4974 assert_eq!(frames[0].payload.len(), max);
4975 assert_eq!(frames[0].flags & FLAG_END_HEADERS, FLAG_END_HEADERS);
4976 assert_eq!(frames[0].flags & FLAG_END_STREAM, 0);
4977
4978 let frames = fragment_header_block(1, &block, max, true);
4980 assert_eq!(frames.len(), 1);
4981 assert_eq!(
4982 frames[0].flags,
4983 FLAG_END_HEADERS | FLAG_END_STREAM,
4984 "exact-fit HEADERS with no body should have END_HEADERS|END_STREAM"
4985 );
4986 }
4987
4988 #[test]
4989 fn fragment_header_block_empty() {
4990 let frames = fragment_header_block(1, &[], 16_384, true);
4993 assert_eq!(frames.len(), 1);
4994 assert_eq!(frames[0].typ, F_HEADERS);
4995 assert!(frames[0].payload.is_empty());
4996 assert_eq!(frames[0].flags, FLAG_END_HEADERS | FLAG_END_STREAM);
4997 }
4998
4999 #[test]
5000 fn fragment_header_block_small_under_cap() {
5001 let block = vec![0x82, 0x86, 0x84]; let frames = fragment_header_block(1, &block, 16_384, false);
5005 assert_eq!(frames.len(), 1);
5006 assert_eq!(frames[0].typ, F_HEADERS);
5007 assert_eq!(frames[0].payload, block);
5008 assert_eq!(frames[0].flags, FLAG_END_HEADERS);
5009 }
5010
5011 #[test]
5012 fn next_data_chunk_size_clamps_to_min_of_three() {
5013 assert_eq!(next_data_chunk_size(16_384, 65_535, 100), 100);
5015 assert_eq!(next_data_chunk_size(16_384, 65_535, 1_000_000), 16_384);
5016 assert_eq!(next_data_chunk_size(16_384, 1_000, 1_000_000), 1_000);
5017 assert_eq!(next_data_chunk_size(16_384, 5_000, 8_000), 5_000);
5018 }
5019
5020 #[test]
5021 fn next_data_chunk_size_zero_when_window_depleted() {
5022 assert_eq!(next_data_chunk_size(16_384, 0, 100), 0);
5025 assert_eq!(next_data_chunk_size(16_384, -1, 100), 0);
5026 assert_eq!(next_data_chunk_size(16_384, -65_535, 100), 0);
5027 }
5028
5029 #[test]
5030 fn fragment_data_into_chunks() {
5031 fn fragment(body: &[u8], max_frame_size: usize, mut available: i64) -> Vec<Frame> {
5038 let mut out = Vec::new();
5039 let mut remaining = body;
5040 while !remaining.is_empty() {
5041 let n = next_data_chunk_size(max_frame_size, available, remaining.len());
5042 if n == 0 {
5043 break; }
5045 let chunk = &remaining[..n];
5046 remaining = &remaining[n..];
5047 let is_last = remaining.is_empty();
5048 out.push(Frame {
5049 typ: F_DATA,
5050 flags: if is_last { FLAG_END_STREAM } else { 0 },
5051 stream_id: 1,
5052 payload: chunk.to_vec(),
5053 });
5054 available -= n as i64;
5055 }
5056 out
5057 }
5058
5059 let body: Vec<u8> = (0..50_000u32).map(|i| (i & 0xff) as u8).collect();
5061 let frames = fragment(&body, 16_384, 65_535);
5062 assert_eq!(frames.len(), 4);
5064 assert_eq!(frames[0].payload.len(), 16_384);
5065 assert_eq!(frames[1].payload.len(), 16_384);
5066 assert_eq!(frames[2].payload.len(), 16_384);
5067 assert_eq!(frames[3].payload.len(), 50_000 - 3 * 16_384);
5068 assert_eq!(frames[0].flags, 0);
5070 assert_eq!(frames[1].flags, 0);
5071 assert_eq!(frames[2].flags, 0);
5072 assert_eq!(frames[3].flags, FLAG_END_STREAM);
5073 let mut roundtrip = Vec::with_capacity(body.len());
5075 for f in &frames {
5076 roundtrip.extend_from_slice(&f.payload);
5077 }
5078 assert_eq!(roundtrip, body);
5079
5080 let frames = fragment(&body, 16_384, 4_000);
5082 assert_eq!(frames.len(), 1);
5086 assert_eq!(frames[0].payload.len(), 4_000);
5087 assert_eq!(frames[0].flags, 0);
5089
5090 let body = vec![0xab; 16_384];
5092 let frames = fragment(&body, 16_384, 65_535);
5093 assert_eq!(frames.len(), 1);
5094 assert_eq!(frames[0].payload.len(), 16_384);
5095 assert_eq!(frames[0].flags, FLAG_END_STREAM);
5096
5097 let frames = fragment(&[], 16_384, 65_535);
5099 assert!(frames.is_empty());
5100 }
5101
5102 struct FakeTls {
5110 wire_in: Cursor<Vec<u8>>,
5111 wire_out: Vec<u8>,
5112 }
5113
5114 impl FakeTls {
5115 fn new() -> Self {
5116 FakeTls {
5117 wire_in: Cursor::new(Vec::new()),
5118 wire_out: Vec::new(),
5119 }
5120 }
5121 }
5122
5123 impl Read for FakeTls {
5124 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
5125 self.wire_in.read(buf)
5126 }
5127 }
5128 impl Write for FakeTls {
5129 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
5130 self.wire_out.extend_from_slice(buf);
5131 Ok(buf.len())
5132 }
5133 fn flush(&mut self) -> io::Result<()> {
5134 Ok(())
5135 }
5136 }
5137
5138 fn fake_conn() -> Connection<FakeTls> {
5143 Connection {
5144 tls: FakeTls::new(),
5145 peer: PeerSettings::default(),
5146 conn_send_window: ConnSendWindow::new(),
5147 conn_recv_window: ConnRecvWindow::new(OUR_INITIAL_WINDOW),
5148 our_recv_window: OUR_INITIAL_WINDOW,
5149 decoder: Decoder::new(),
5150 encoder: Encoder::new(),
5151 streams: HashMap::new(),
5152 next_stream_id: 1,
5153 goaway_received: None,
5154 expecting_continuation: None,
5155 budget: FloodBudget::default(),
5156 made_progress: false,
5157 tls_info: None,
5158 dial_timing: crate::http::Timing::default(),
5159 }
5160 }
5161
5162 #[test]
5163 fn priority_weight_byte_maps_hints() {
5164 use crate::http::Priority;
5165 assert_eq!(priority_weight_byte(Priority::High), Some(255));
5166 assert_eq!(priority_weight_byte(Priority::Normal), None);
5167 assert_eq!(priority_weight_byte(Priority::Low), Some(0));
5168 }
5169
5170 #[test]
5171 fn priority_hint_emits_priority_frame() {
5172 use crate::http::Priority;
5173 let mut conn = fake_conn();
5174 conn.send_priority_hint(1, Priority::High).unwrap();
5175 let out = &conn.tls.wire_out;
5176 assert_eq!(out.len(), 14, "expected one PRIORITY frame");
5178 assert_eq!(out[3], F_PRIORITY, "frame type should be PRIORITY");
5179 assert_eq!(out[13], 255, "weight byte should be max for High");
5180 let mut c2 = fake_conn();
5182 c2.send_priority_hint(3, Priority::Normal).unwrap();
5183 assert!(
5184 c2.tls.wire_out.is_empty(),
5185 "Normal should send no PRIORITY frame"
5186 );
5187 }
5188
5189 #[test]
5190 fn apply_dial_timing_copies_phases() {
5191 use std::time::Duration;
5192 let mut conn = fake_conn();
5193 conn.dial_timing = crate::http::Timing {
5194 namelookup: Some(Duration::from_millis(1)),
5195 connect: Some(Duration::from_millis(2)),
5196 appconnect: Some(Duration::from_millis(3)),
5197 pretransfer: Some(Duration::from_millis(3)),
5198 ..Default::default()
5199 };
5200 let mut resp = crate::http::Response {
5201 status: 200,
5202 reason: String::new(),
5203 version: "HTTP/2".into(),
5204 headers: Vec::new(),
5205 body: Vec::new(),
5206 timing: crate::http::Timing::default(),
5207 final_url: String::new(),
5208 tls: None,
5209 };
5210 apply_dial_timing(&mut resp, &conn);
5211 assert_eq!(resp.timing.namelookup, Some(Duration::from_millis(1)));
5212 assert_eq!(resp.timing.connect, Some(Duration::from_millis(2)));
5213 assert_eq!(resp.timing.appconnect, Some(Duration::from_millis(3)));
5214 assert_eq!(resp.timing.pretransfer, Some(Duration::from_millis(3)));
5215 }
5216
5217 #[test]
5218 fn connection_process_settings_acks_and_applies() {
5219 let payload =
5226 settings_payload(&[(S_MAX_FRAME_SIZE, 32_768), (S_INITIAL_WINDOW_SIZE, 131_072)]);
5227 let frame = Frame {
5228 typ: F_SETTINGS,
5229 flags: 0,
5230 stream_id: 0,
5231 payload,
5232 };
5233 let mut conn = fake_conn();
5234 let outcome = conn.process_frame(frame, None).unwrap();
5235 assert_eq!(outcome, DispatchOutcome::Continue);
5236 assert_eq!(conn.peer.max_frame_size, 32_768);
5237 assert_eq!(conn.peer.initial_window_size, 131_072);
5238 assert_eq!(conn.conn_send_window.available, 65_535); assert_eq!(conn.tls.wire_out.len(), 9);
5242 let mut cur = Cursor::new(conn.tls.wire_out.clone());
5243 let ack = read_frame(&mut cur).unwrap();
5244 assert_eq!(ack.typ, F_SETTINGS);
5245 assert_eq!(ack.flags, FLAG_ACK);
5246 assert_eq!(ack.stream_id, 0);
5247 assert!(ack.payload.is_empty());
5248 }
5249
5250 #[test]
5251 fn connection_process_window_update_replenishes_send_window() {
5252 let mut conn = fake_conn();
5255 let id = conn.open_stream().unwrap();
5256 conn.process_frame(window_update_frame(id, 10_000), None)
5257 .unwrap();
5258 assert_eq!(
5259 conn.streams.get(&id).unwrap().send_window.available,
5260 65_535 + 10_000
5261 );
5262 assert_eq!(conn.conn_send_window.available, 65_535);
5263
5264 conn.process_frame(window_update_frame(0, 5_000), None)
5265 .unwrap();
5266 assert_eq!(conn.conn_send_window.available, 65_535 + 5_000);
5267 }
5268
5269 #[test]
5272 fn stream_state_open_to_half_closed_local_on_end_stream_send() {
5273 let s = StreamState::Open;
5275 assert_eq!(
5276 s.send_data(true).unwrap(),
5277 StreamState::HalfClosedLocal
5278 );
5279 assert_eq!(
5281 StreamState::Open.send_data(false).unwrap(),
5282 StreamState::Open
5283 );
5284 }
5285
5286 #[test]
5287 fn stream_state_recv_data_in_idle_is_error() {
5288 let err = StreamState::Idle.recv_data(false).unwrap_err();
5290 assert!(matches!(err, Error::BadResponse(_)));
5291 }
5292
5293 #[test]
5294 fn stream_state_recv_headers_on_closed_stream_is_ignored() {
5295 assert_eq!(
5299 StreamState::Closed.recv_headers(true).unwrap(),
5300 StreamState::Closed
5301 );
5302 }
5303
5304 #[test]
5307 fn next_stream_id_allocates_odd_only() {
5308 let mut conn = fake_conn();
5311 let ids: Vec<u32> = (0..4).map(|_| conn.open_stream().unwrap()).collect();
5312 assert_eq!(ids, vec![1, 3, 5, 7]);
5313 }
5314
5315 #[test]
5316 fn open_stream_refuses_at_max_concurrent() {
5317 let mut conn = fake_conn();
5318 conn.peer.max_concurrent_streams = 2;
5319 assert!(conn.open_stream().is_ok());
5320 assert!(conn.open_stream().is_ok());
5321 let err = conn.open_stream().unwrap_err();
5322 assert!(matches!(err, Error::BadResponse(_)));
5323 }
5324
5325 #[test]
5326 fn open_stream_refuses_after_goaway() {
5327 let mut conn = fake_conn();
5330 conn.goaway_received = Some(3);
5331 assert_eq!(conn.open_stream().unwrap(), 1);
5332 assert_eq!(conn.open_stream().unwrap(), 3);
5333 let err = conn.open_stream().unwrap_err();
5334 assert!(matches!(err, Error::BadResponse(_)));
5335 }
5336
5337 fn synth_status_200_headers(id: u32, end_stream: bool) -> Frame {
5341 let payload = vec![0x88];
5343 let mut flags = FLAG_END_HEADERS;
5344 if end_stream {
5345 flags |= FLAG_END_STREAM;
5346 }
5347 Frame {
5348 typ: F_HEADERS,
5349 flags,
5350 stream_id: id,
5351 payload,
5352 }
5353 }
5354
5355 fn synth_data(id: u32, body: &[u8], end_stream: bool) -> Frame {
5356 Frame {
5357 typ: F_DATA,
5358 flags: if end_stream { FLAG_END_STREAM } else { 0 },
5359 stream_id: id,
5360 payload: body.to_vec(),
5361 }
5362 }
5363
5364 #[test]
5365 fn dispatch_frame_routes_to_correct_stream() {
5366 let mut conn = fake_conn();
5369 let id_a = conn.open_stream().unwrap();
5370 let id_b = conn.open_stream().unwrap();
5371 conn.streams.get_mut(&id_a).unwrap().state = StreamState::Open;
5373 conn.streams.get_mut(&id_b).unwrap().state = StreamState::Open;
5374
5375 conn.process_frame(synth_status_200_headers(id_a, false), None)
5376 .unwrap();
5377 conn.process_frame(synth_status_200_headers(id_b, false), None)
5378 .unwrap();
5379 conn.process_frame(synth_data(id_a, b"aaa", false), None)
5380 .unwrap();
5381 conn.process_frame(synth_data(id_b, b"bbbb", false), None)
5382 .unwrap();
5383 conn.process_frame(synth_data(id_a, b"AAA", true), None)
5384 .unwrap();
5385 conn.process_frame(synth_data(id_b, b"BBBB", true), None)
5386 .unwrap();
5387
5388 assert_eq!(conn.streams.get(&id_a).unwrap().body, b"aaaAAA");
5389 assert_eq!(conn.streams.get(&id_b).unwrap().body, b"bbbbBBBB");
5390 }
5391
5392 #[test]
5393 fn dispatch_data_on_unknown_stream_is_silently_dropped() {
5394 let mut conn = fake_conn();
5397 let outcome = conn
5398 .process_frame(synth_data(7, b"orphaned", false), None)
5399 .unwrap();
5400 assert_eq!(outcome, DispatchOutcome::Continue);
5401 assert!(conn.streams.is_empty());
5403 assert!(conn.conn_recv_window.available <= OUR_INITIAL_WINDOW);
5406 }
5407
5408 #[test]
5409 fn inbound_data_exceeding_conn_window_is_flow_control_error() {
5410 let mut conn = fake_conn();
5413 let id = conn.open_stream().unwrap();
5414 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5415 conn.process_frame(synth_status_200_headers(id, false), None)
5416 .unwrap();
5417
5418 let overrun = vec![0u8; OUR_INITIAL_WINDOW as usize + 1];
5420 let err = conn
5421 .process_frame(synth_data(id, &overrun, false), None)
5422 .unwrap_err();
5423 match err {
5424 Error::BadResponse(m) => assert!(
5425 m.contains("flow-control window exceeded"),
5426 "unexpected message: {m}"
5427 ),
5428 other => panic!("expected BadResponse, got {other:?}"),
5429 }
5430 }
5431
5432 #[test]
5433 fn inbound_data_exceeding_stream_window_is_flow_control_error() {
5434 let mut conn = fake_conn();
5438 let id = conn.open_stream().unwrap();
5439 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5440 conn.process_frame(synth_status_200_headers(id, false), None)
5441 .unwrap();
5442
5443 conn.conn_recv_window.available = i64::from(u32::MAX);
5445
5446 let overrun = vec![0u8; OUR_INITIAL_WINDOW as usize + 1];
5447 let err = conn
5448 .process_frame(synth_data(id, &overrun, false), None)
5449 .unwrap_err();
5450 match err {
5451 Error::BadResponse(m) => assert!(
5452 m.contains("flow-control window exceeded"),
5453 "unexpected message: {m}"
5454 ),
5455 other => panic!("expected BadResponse, got {other:?}"),
5456 }
5457 assert!(conn.streams.get(&id).unwrap().recv_window.available < 0);
5459 }
5460
5461 #[test]
5462 fn inbound_data_filling_window_exactly_is_accepted() {
5463 let mut conn = fake_conn();
5466 let id = conn.open_stream().unwrap();
5467 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5468 conn.process_frame(synth_status_200_headers(id, false), None)
5469 .unwrap();
5470
5471 let exact = vec![0u8; OUR_INITIAL_WINDOW as usize];
5472 conn.process_frame(synth_data(id, &exact, false), None)
5474 .unwrap();
5475 assert_eq!(conn.streams.get(&id).unwrap().body.len(), exact.len());
5476 }
5477
5478 #[test]
5479 fn dispatch_continuation_on_wrong_stream_is_protocol_error() {
5480 let mut conn = fake_conn();
5483 let id1 = conn.open_stream().unwrap();
5484 let id3 = conn.open_stream().unwrap();
5485 assert_eq!(id1, 1);
5486 assert_eq!(id3, 3);
5487 conn.streams.get_mut(&id1).unwrap().state = StreamState::Open;
5488 conn.streams.get_mut(&id3).unwrap().state = StreamState::Open;
5489
5490 let frame = Frame {
5492 typ: F_HEADERS,
5493 flags: 0, stream_id: id1,
5495 payload: vec![0x88], };
5497 conn.process_frame(frame, None).unwrap();
5498 assert_eq!(conn.expecting_continuation, Some(id1));
5499
5500 let bad = Frame {
5502 typ: F_CONTINUATION,
5503 flags: FLAG_END_HEADERS,
5504 stream_id: id3,
5505 payload: vec![],
5506 };
5507 let err = conn.process_frame(bad, None).unwrap_err();
5508 assert!(matches!(err, Error::BadResponse(_)));
5509 }
5510
5511 #[test]
5512 fn data_frames_past_body_cap_are_rejected() {
5513 let mut conn = fake_conn();
5516 let id = conn.open_stream().unwrap();
5517 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5518 conn.streams.get_mut(&id).unwrap().body = vec![0u8; MAX_RESPONSE_BYTES - 2];
5521 let err = conn
5522 .process_frame(synth_data(id, b"abc", false), None)
5523 .unwrap_err();
5524 assert!(matches!(err, Error::BadResponse(_)));
5525 assert_eq!(
5527 conn.streams.get(&id).unwrap().body.len(),
5528 MAX_RESPONSE_BYTES - 2
5529 );
5530 }
5531
5532 #[test]
5533 fn empty_data_flood_is_bounded() {
5534 let mut conn = fake_conn();
5540 let id = conn.open_stream().unwrap();
5541 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5542 conn.process_frame(synth_status_200_headers(id, false), None)
5543 .unwrap();
5544 let mut err = None;
5547 for _ in 0..(MAX_NO_PROGRESS_FRAMES as usize + 10) {
5548 match conn.process_frame(synth_data(id, b"", false), None) {
5549 Ok(_) => {}
5550 Err(e) => {
5551 err = Some(e);
5552 break;
5553 }
5554 }
5555 }
5556 let err = err.expect("empty-DATA flood was not bounded");
5557 match err {
5558 Error::BadResponse(m) => {
5559 assert!(m.contains("no forward progress"), "unexpected message: {m}")
5560 }
5561 other => panic!("expected BadResponse, got {other:?}"),
5562 }
5563 assert_eq!(conn.streams.get(&id).unwrap().body.len(), 0);
5566 assert_eq!(conn.streams.get(&id).unwrap().state, StreamState::Open);
5567 }
5568
5569 #[test]
5570 fn process_data_streams_body_to_sink() {
5571 let mut conn = fake_conn();
5574 let id = conn.open_stream().unwrap();
5575 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5576 conn.process_frame(synth_status_200_headers(id, false), None)
5577 .unwrap();
5578 let mut sink: Vec<u8> = Vec::new();
5579 conn.process_frame(synth_data(id, b"hello ", false), Some(&mut sink))
5580 .unwrap();
5581 conn.process_frame(synth_data(id, b"world", true), Some(&mut sink))
5582 .unwrap();
5583 assert_eq!(sink, b"hello world");
5584 let s = conn.streams.get(&id).unwrap();
5585 assert_eq!(s.body.len(), 0, "streamed body must not be buffered");
5586 assert_eq!(s.streamed_len, 11);
5587 }
5588
5589 #[test]
5590 fn body_byte_resets_no_progress_counter() {
5591 let mut conn = fake_conn();
5596 let id = conn.open_stream().unwrap();
5597 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5598 conn.process_frame(synth_status_200_headers(id, false), None)
5599 .unwrap();
5600
5601 for _ in 0..(MAX_NO_PROGRESS_FRAMES - 1) {
5602 conn.process_frame(synth_data(id, b"", false), None)
5603 .unwrap();
5604 }
5605 assert_eq!(conn.budget.no_progress, MAX_NO_PROGRESS_FRAMES - 1);
5606 conn.process_frame(synth_data(id, b"x", false), None)
5608 .unwrap();
5609 assert_eq!(conn.budget.no_progress, 0);
5610 assert_eq!(conn.streams.get(&id).unwrap().body, b"x");
5611 }
5612
5613 #[test]
5614 fn settings_flood_is_bounded() {
5615 let mut conn = fake_conn();
5619 let mut err = None;
5620 for _ in 0..(MAX_SETTINGS_FRAMES as usize + 10) {
5621 let f = Frame {
5622 typ: F_SETTINGS,
5623 flags: 0,
5624 stream_id: 0,
5625 payload: Vec::new(),
5626 };
5627 if let Err(e) = conn.process_frame(f, None) {
5628 err = Some(e);
5629 break;
5630 }
5631 }
5632 match err.expect("SETTINGS flood was not bounded") {
5633 Error::BadResponse(m) => {
5634 assert!(m.contains("SETTINGS"), "unexpected message: {m}")
5635 }
5636 other => panic!("expected BadResponse, got {other:?}"),
5637 }
5638 }
5639
5640 #[test]
5641 fn ping_flood_is_bounded() {
5642 let mut conn = fake_conn();
5644 let mut err = None;
5645 for _ in 0..(MAX_PING_FRAMES as usize + 10) {
5646 let f = Frame {
5647 typ: F_PING,
5648 flags: 0,
5649 stream_id: 0,
5650 payload: vec![0u8; 8],
5651 };
5652 if let Err(e) = conn.process_frame(f, None) {
5653 err = Some(e);
5654 break;
5655 }
5656 }
5657 match err.expect("PING flood was not bounded") {
5658 Error::BadResponse(m) => assert!(m.contains("PING"), "unexpected message: {m}"),
5659 other => panic!("expected BadResponse, got {other:?}"),
5660 }
5661 }
5662
5663 #[test]
5664 fn rst_stream_flood_is_bounded() {
5665 let mut conn = fake_conn();
5670 let mut err = None;
5671 for i in 0..(MAX_RST_STREAM_FRAMES as usize + 10) {
5672 let f = synth_rst((2 * i as u32) + 1001, 0);
5675 if let Err(e) = conn.process_frame(f, None) {
5676 err = Some(e);
5677 break;
5678 }
5679 }
5680 match err.expect("RST_STREAM flood was not bounded") {
5681 Error::BadResponse(m) => {
5682 assert!(m.contains("RST_STREAM"), "unexpected message: {m}")
5683 }
5684 other => panic!("expected BadResponse, got {other:?}"),
5685 }
5686 }
5687
5688 #[test]
5689 fn continuation_flood_is_bounded() {
5690 let mut conn = fake_conn();
5693 let id = conn.open_stream().unwrap();
5694 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
5695 conn.process_frame(
5698 Frame {
5699 typ: F_HEADERS,
5700 flags: 0,
5701 stream_id: id,
5702 payload: vec![0u8; 8 * 1024],
5703 },
5704 None,
5705 )
5706 .unwrap();
5707 let chunk = vec![0u8; 16 * 1024];
5710 let mut hit_cap = false;
5711 for _ in 0..(MAX_HEADERS_BUF / chunk.len() + 4) {
5712 let r = conn.process_frame(
5713 Frame {
5714 typ: F_CONTINUATION,
5715 flags: 0,
5716 stream_id: id,
5717 payload: chunk.clone(),
5718 },
5719 None,
5720 );
5721 if let Err(Error::BadResponse(_)) = r {
5722 hit_cap = true;
5723 break;
5724 }
5725 r.unwrap();
5726 }
5727 assert!(hit_cap, "CONTINUATION flood was not bounded");
5728 assert!(conn.streams.get(&id).unwrap().headers_buf.len() <= MAX_HEADERS_BUF);
5729 }
5730
5731 #[test]
5732 fn hpack_decompression_bomb_is_rejected() {
5733 let mut dec = Decoder::new();
5738 let mut block: Vec<u8> = Vec::new();
5739 let name = b"a";
5743 let value = vec![b'x'; 4096];
5744 let mut entry = Vec::new();
5746 entry.push(0x40); entry.push(name.len() as u8); entry.extend_from_slice(name);
5749 encode_int_local(value.len() as u64, 7, 0x00, &mut entry);
5752 entry.extend_from_slice(&value);
5753 for _ in 0..200 {
5755 block.extend_from_slice(&entry);
5756 }
5757 let err = dec.decode_block(&block).unwrap_err();
5758 assert!(matches!(err, Error::BadResponse(_)));
5759 }
5760
5761 fn encode_int_local(mut value: u64, prefix_bits: u8, first_byte_high: u8, out: &mut Vec<u8>) {
5763 let max_prefix = (1u64 << prefix_bits) - 1;
5764 if value < max_prefix {
5765 out.push(first_byte_high | value as u8);
5766 return;
5767 }
5768 out.push(first_byte_high | max_prefix as u8);
5769 value -= max_prefix;
5770 while value >= 128 {
5771 out.push(((value & 0x7f) as u8) | 0x80);
5772 value >>= 7;
5773 }
5774 out.push(value as u8);
5775 }
5776
5777 fn fake_arc_conn() -> Arc<Mutex<Connection<FakeTls>>> {
5784 Arc::new(Mutex::new(fake_conn()))
5785 }
5786
5787 fn url_key(url: &str) -> PoolKey {
5788 let req = Request::new("GET", url).unwrap();
5789 PoolKey::from_request(&req)
5790 }
5791
5792 #[test]
5793 fn pool_key_round_trip() {
5794 let a = url_key("https://example.com/a");
5796 let b = url_key("https://example.com/b"); assert_eq!(a, b);
5798
5799 let c = url_key("https://example.com:8443/a");
5800 assert_ne!(a, c, "port differs");
5801
5802 let d = url_key("https://other.example/a");
5803 assert_ne!(a, d, "host differs");
5804 }
5805
5806 #[test]
5807 fn pool_checkout_empty_returns_none() {
5808 let mut pool: PoolInner<FakeTls> = PoolInner::new();
5809 let k = url_key("https://example.com/");
5810 assert!(pool.checkout(&k).is_none());
5811 }
5812
5813 #[test]
5814 fn pool_release_then_checkout_returns_same_conn() {
5815 let mut pool: PoolInner<FakeTls> = PoolInner::new();
5817 let k = url_key("https://example.com/");
5818 let arc = fake_arc_conn();
5819 let raw_in = Arc::as_ptr(&arc) as usize;
5820 pool.release(k.clone(), arc);
5821
5822 let got = pool.checkout(&k).expect("checkout after release");
5823 let raw_out = Arc::as_ptr(&got) as usize;
5824 assert_eq!(raw_in, raw_out, "pool returned a different Arc");
5825
5826 assert!(pool.checkout(&k).is_none());
5828 }
5829
5830 #[test]
5831 fn pool_per_key_cap_drops_overflow() {
5832 let _g = crate::pool::CAP_TEST_LOCK
5835 .lock()
5836 .unwrap_or_else(|e| e.into_inner());
5837 crate::pool::configure(4, 32);
5838 let cap = crate::pool::per_key_cap();
5839 let mut pool: PoolInner<FakeTls> = PoolInner::new();
5840 let k = url_key("https://example.com/");
5841 for _ in 0..(cap + 2) {
5842 pool.release(k.clone(), fake_arc_conn());
5843 }
5844 let mut popped = 0;
5845 while pool.checkout(&k).is_some() {
5846 popped += 1;
5847 }
5848 assert_eq!(popped, cap);
5849 }
5850
5851 #[test]
5852 fn pool_global_cap_drops_overflow() {
5853 let _g = crate::pool::CAP_TEST_LOCK
5856 .lock()
5857 .unwrap_or_else(|e| e.into_inner());
5858 crate::pool::configure(4, 32);
5859 let cap = crate::pool::global_cap();
5860 let mut pool: PoolInner<FakeTls> = PoolInner::new();
5861 for i in 0..(cap * 2) {
5862 let k = url_key(&format!("https://h{i}.example/"));
5863 pool.release(k, fake_arc_conn());
5864 }
5865 assert!(
5866 pool.total_len() <= cap,
5867 "pool grew past global cap: {} > {}",
5868 pool.total_len(),
5869 cap
5870 );
5871 assert_eq!(pool.total_len(), cap);
5874 }
5875
5876 #[test]
5877 fn connection_is_usable_false_after_goaway() {
5878 let mut conn = fake_conn();
5879 conn.goaway_received = Some(0);
5880 assert!(
5881 conn.streams.is_empty(),
5882 "precondition: fresh conn has no streams"
5883 );
5884 assert!(!conn.is_usable());
5885 }
5886
5887 #[test]
5888 fn connection_is_usable_true_initially() {
5889 let conn = fake_conn();
5890 assert!(conn.is_usable());
5891 }
5892
5893 fn synth_full_response(id: u32, body: &[u8]) -> Vec<Frame> {
5904 vec![
5905 synth_status_200_headers(id, false),
5906 synth_data(id, body, true),
5907 ]
5908 }
5909
5910 fn h2_get(url: &str) -> Request {
5911 Request::new("GET", url).unwrap()
5912 }
5913
5914 #[test]
5915 fn sequential_reuse_advances_stream_ids_and_demuxes_bodies() {
5916 let mut inbound = Vec::new();
5919 inbound.extend(synth_full_response(1, b"first"));
5920 inbound.extend(synth_full_response(3, b"second"));
5921 inbound.extend(synth_full_response(5, b"third"));
5922 let mut conn = fake_conn_with_inbound(&inbound);
5923
5924 let req = h2_get("https://example.com/");
5925
5926 assert_eq!(conn.next_stream_id, 1);
5928 let r1 = run_one_request(&mut conn, &req, &mut std::io::sink()).unwrap();
5929 assert_eq!(r1.status, 200);
5930 assert_eq!(r1.body, b"first");
5931 assert!(conn.streams.is_empty(), "stream 1 not reaped after reuse");
5933 assert_eq!(conn.next_stream_id, 3);
5934 assert!(conn.is_usable());
5935
5936 let r2 = run_one_request(&mut conn, &req, &mut std::io::sink()).unwrap();
5938 assert_eq!(r2.status, 200);
5939 assert_eq!(r2.body, b"second");
5940 assert!(conn.streams.is_empty());
5941 assert_eq!(conn.next_stream_id, 5);
5942 assert!(conn.is_usable());
5943
5944 let r3 = run_one_request(&mut conn, &req, &mut std::io::sink()).unwrap();
5946 assert_eq!(r3.body, b"third");
5947 assert_eq!(conn.next_stream_id, 7);
5948 assert!(conn.is_usable());
5949
5950 let header_ids: Vec<u32> = drain_wire_out(&conn)
5954 .into_iter()
5955 .filter(|f| f.typ == F_HEADERS)
5956 .map(|f| f.stream_id)
5957 .collect();
5958 assert_eq!(header_ids, vec![1, 3, 5]);
5959 }
5960
5961 #[test]
5962 fn run_one_request_emits_curl_style_verbose_trace() {
5963 let mut hdr_payload = Vec::new();
5969 let mut enc = Encoder::new();
5970 enc.encode_header(&mut hdr_payload, ":status", "200");
5971 enc.encode_header(&mut hdr_payload, "content-type", "text/plain");
5972 let headers_frame = Frame {
5973 typ: F_HEADERS,
5974 flags: FLAG_END_HEADERS,
5975 stream_id: 1,
5976 payload: hdr_payload,
5977 };
5978 let inbound = vec![headers_frame, synth_data(1, b"hello world", true)];
5979 let mut conn = fake_conn_with_inbound(&inbound);
5980
5981 let req = h2_get("https://example.com/path");
5982 let mut trace: Vec<u8> = Vec::new();
5983 let resp = run_one_request(&mut conn, &req, &mut trace).unwrap();
5984 assert_eq!(resp.status, 200);
5985 assert_eq!(resp.body, b"hello world");
5986
5987 let t = String::from_utf8(trace).expect("trace is utf-8");
5988 assert!(
5990 t.contains("> GET /path HTTP/2"),
5991 "missing request line in trace:\n{t}"
5992 );
5993 assert!(
5994 t.contains("> Host: example.com"),
5995 "missing Host line in trace:\n{t}"
5996 );
5997 assert!(
5998 t.contains("> accept: */*"),
5999 "missing default accept header in trace:\n{t}"
6000 );
6001 assert!(
6003 t.contains("< HTTP/2 200"),
6004 "missing response status line in trace:\n{t}"
6005 );
6006 assert!(
6007 t.contains("< content-type: text/plain"),
6008 "missing response header line in trace:\n{t}"
6009 );
6010 assert!(
6011 t.contains("* Received 11 body bytes"),
6012 "missing received-bytes notice in trace:\n{t}"
6013 );
6014 }
6015
6016 #[test]
6017 fn goaway_between_requests_marks_connection_non_reusable() {
6018 let mut inbound = Vec::new();
6022 inbound.extend(synth_full_response(1, b"ok"));
6023 let mut goaway_payload = Vec::new();
6025 goaway_payload.extend_from_slice(&1u32.to_be_bytes()); goaway_payload.extend_from_slice(&0u32.to_be_bytes()); inbound.push(Frame {
6028 typ: F_GOAWAY,
6029 flags: 0,
6030 stream_id: 0,
6031 payload: goaway_payload,
6032 });
6033 let mut conn = fake_conn_with_inbound(&inbound);
6034
6035 let req = h2_get("https://example.com/");
6036 let r1 = run_one_request(&mut conn, &req, &mut std::io::sink()).unwrap();
6037 assert_eq!(r1.body, b"ok");
6038 assert!(conn.is_usable(), "no GOAWAY seen yet — still reusable");
6039
6040 let outcome = conn.read_and_dispatch(None).unwrap();
6042 assert_eq!(outcome, DispatchOutcome::Continue);
6043 assert_eq!(conn.goaway_received, Some(1));
6044 assert!(
6045 !conn.is_usable(),
6046 "GOAWAY must make the connection non-reusable"
6047 );
6048 }
6049
6050 #[test]
6051 fn prune_completed_streams_drops_terminal_entries_only() {
6052 let mut conn = fake_conn();
6055 let open_id = conn.open_stream().unwrap();
6056 let done_id = conn.open_stream().unwrap();
6057 {
6059 let s = conn.streams.get_mut(&done_id).unwrap();
6060 s.state = StreamState::Closed;
6061 s.response_headers = Some(vec![(":status".into(), "200".into())]);
6062 s.end_stream_recv = true;
6063 }
6064 conn.streams.get_mut(&open_id).unwrap().state = StreamState::Open;
6065
6066 conn.prune_completed_streams();
6067 assert!(
6068 conn.streams.contains_key(&open_id),
6069 "open stream was reaped"
6070 );
6071 assert!(
6072 !conn.streams.contains_key(&done_id),
6073 "closed stream was not reaped"
6074 );
6075 }
6076
6077 #[test]
6078 fn initial_window_size_delta_applies_to_all_streams() {
6079 let mut conn = fake_conn();
6083 let id1 = conn.open_stream().unwrap();
6084 let id2 = conn.open_stream().unwrap();
6085
6086 let payload = settings_payload(&[(S_INITIAL_WINDOW_SIZE, 131_072)]);
6087 let frame = Frame {
6088 typ: F_SETTINGS,
6089 flags: 0,
6090 stream_id: 0,
6091 payload,
6092 };
6093 conn.process_frame(frame, None).unwrap();
6094
6095 let expect = 65_535 + (131_072 - 65_535);
6096 assert_eq!(
6097 conn.streams.get(&id1).unwrap().send_window.available,
6098 expect
6099 );
6100 assert_eq!(
6101 conn.streams.get(&id2).unwrap().send_window.available,
6102 expect
6103 );
6104 assert_eq!(conn.conn_send_window.available, 65_535);
6105 }
6106
6107 fn fake_conn_with_inbound(frames: &[Frame]) -> Connection<FakeTls> {
6117 let mut bytes = Vec::new();
6118 for f in frames {
6119 write_frame(&mut bytes, f).unwrap();
6120 }
6121 let mut conn = fake_conn();
6122 conn.tls.wire_in = Cursor::new(bytes);
6123 conn
6124 }
6125
6126 fn drain_wire_out(conn: &Connection<FakeTls>) -> Vec<Frame> {
6128 let mut cur = Cursor::new(conn.tls.wire_out.clone());
6129 let mut out = Vec::new();
6130 while (cur.position() as usize) < conn.tls.wire_out.len() {
6131 out.push(read_frame(&mut cur).unwrap());
6132 }
6133 out
6134 }
6135
6136 fn h2_request_with_body(body: Vec<u8>) -> Request {
6137 let mut req = Request::new("POST", "https://example.com/upload").unwrap();
6138 req.body = body;
6139 req
6140 }
6141
6142 #[test]
6143 fn send_body_splits_across_window_updates() {
6144 let body = (0..12u8).collect::<Vec<u8>>();
6151 let req = h2_request_with_body(body.clone());
6152
6153 let inbound = vec![window_update_frame(1, 5), window_update_frame(1, 5)];
6155 let mut conn = fake_conn_with_inbound(&inbound);
6156 conn.peer.initial_window_size = 5;
6159
6160 let id = conn.open_stream().unwrap();
6161 assert_eq!(conn.streams.get(&id).unwrap().send_window.available, 5);
6162
6163 conn.send_request_on(id, &req).unwrap();
6164
6165 let frames = drain_wire_out(&conn);
6167 let data: Vec<&Frame> = frames.iter().filter(|f| f.typ == F_DATA).collect();
6168 assert_eq!(
6169 data.len(),
6170 3,
6171 "12-byte body under a 5-octet window must split into 5+5+2"
6172 );
6173 assert_eq!(data[0].payload.len(), 5);
6174 assert_eq!(data[1].payload.len(), 5);
6175 assert_eq!(data[2].payload.len(), 2);
6176 assert_eq!(data[0].flags & FLAG_END_STREAM, 0);
6178 assert_eq!(data[1].flags & FLAG_END_STREAM, 0);
6179 assert_eq!(data[2].flags & FLAG_END_STREAM, FLAG_END_STREAM);
6180 let mut reassembled = Vec::new();
6182 for d in &data {
6183 reassembled.extend_from_slice(&d.payload);
6184 }
6185 assert_eq!(reassembled, body);
6186
6187 let s = conn.streams.get(&id).unwrap();
6191 assert_eq!(s.send_window.available, 3, "5+5 granted, 12 consumed");
6192 assert_eq!(conn.conn_send_window.available, 65_535 - 12);
6193 }
6194
6195 #[test]
6196 fn send_body_blocks_on_conn_window_too() {
6197 let body = (0..10u8).collect::<Vec<u8>>();
6202 let req = h2_request_with_body(body.clone());
6203
6204 let inbound = vec![window_update_frame(0, 8)];
6205 let mut conn = fake_conn_with_inbound(&inbound);
6206 conn.conn_send_window.available = 4;
6207
6208 let id = conn.open_stream().unwrap();
6209 conn.send_request_on(id, &req).unwrap();
6210
6211 let frames = drain_wire_out(&conn);
6212 let data: Vec<&Frame> = frames.iter().filter(|f| f.typ == F_DATA).collect();
6213 assert_eq!(data.len(), 2, "conn window of 4 then +8 splits 10 into 4+6");
6214 assert_eq!(data[0].payload.len(), 4);
6215 assert_eq!(data[1].payload.len(), 6);
6216 assert_eq!(data[1].flags & FLAG_END_STREAM, FLAG_END_STREAM);
6217 assert_eq!(conn.conn_send_window.available, 2);
6219 }
6220
6221 #[test]
6222 fn recv_data_replenishes_window_on_the_wire() {
6223 let mut conn = fake_conn();
6228 let id = conn.open_stream().unwrap();
6229 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
6230
6231 let big = vec![0xa5u8; 40_000];
6234 conn.process_frame(synth_data(id, &big, false), None)
6235 .unwrap();
6236
6237 let out = drain_wire_out(&conn);
6238 let updates: Vec<&Frame> = out.iter().filter(|f| f.typ == F_WINDOW_UPDATE).collect();
6239 assert_eq!(
6240 updates.len(),
6241 2,
6242 "one conn-level and one stream-level WINDOW_UPDATE expected"
6243 );
6244 let conn_update = updates.iter().find(|f| f.stream_id == 0).unwrap();
6245 let stream_update = updates.iter().find(|f| f.stream_id == id).unwrap();
6246 assert_eq!(parse_window_update(&conn_update.payload).unwrap(), 40_000);
6248 assert_eq!(parse_window_update(&stream_update.payload).unwrap(), 40_000);
6249 assert_eq!(conn.conn_recv_window.available, OUR_INITIAL_WINDOW);
6251 assert_eq!(
6252 conn.streams.get(&id).unwrap().recv_window.available,
6253 OUR_INITIAL_WINDOW
6254 );
6255 }
6256
6257 #[test]
6258 fn recv_small_data_does_not_replenish() {
6259 let mut conn = fake_conn();
6263 let id = conn.open_stream().unwrap();
6264 conn.streams.get_mut(&id).unwrap().state = StreamState::Open;
6265
6266 conn.process_frame(synth_data(id, b"hello", false), None)
6267 .unwrap();
6268 let out = drain_wire_out(&conn);
6269 assert!(
6270 out.iter().all(|f| f.typ != F_WINDOW_UPDATE),
6271 "no WINDOW_UPDATE should be emitted while windows stay above half"
6272 );
6273 assert_eq!(conn.conn_recv_window.available, OUR_INITIAL_WINDOW - 5);
6274 assert_eq!(
6275 conn.streams.get(&id).unwrap().recv_window.available,
6276 OUR_INITIAL_WINDOW - 5
6277 );
6278 }
6279
6280 #[test]
6281 fn dispatch_zero_increment_window_update_conn_is_error() {
6282 let mut conn = fake_conn();
6285 let frame = window_update_frame(0, 0);
6286 let err = conn.process_frame(frame, None).unwrap_err();
6287 assert!(matches!(err, Error::BadResponse(_)));
6288 }
6289
6290 #[test]
6291 fn dispatch_zero_increment_window_update_stream_is_error() {
6292 let mut conn = fake_conn();
6295 let id = conn.open_stream().unwrap();
6296 let frame = window_update_frame(id, 0);
6297 let err = conn.process_frame(frame, None).unwrap_err();
6298 assert!(matches!(err, Error::BadResponse(_)));
6299 }
6300
6301 #[test]
6302 fn dispatch_window_update_overflow_conn_is_error() {
6303 let mut conn = fake_conn();
6307 conn.conn_send_window.available = WINDOW_MAX - 1;
6308 let err = conn
6309 .process_frame(window_update_frame(0, 5), None)
6310 .unwrap_err();
6311 assert!(matches!(err, Error::BadResponse(_)));
6312 }
6313
6314 #[test]
6315 fn dispatch_window_update_overflow_stream_is_error() {
6316 let mut conn = fake_conn();
6318 let id = conn.open_stream().unwrap();
6319 conn.streams.get_mut(&id).unwrap().send_window.available = WINDOW_MAX - 1;
6320 let err = conn
6321 .process_frame(window_update_frame(id, 5), None)
6322 .unwrap_err();
6323 assert!(matches!(err, Error::BadResponse(_)));
6324 }
6325
6326 #[test]
6327 fn settings_initial_window_change_lets_stalled_send_proceed() {
6328 let body = vec![0x11u8; 6];
6333 let req = h2_request_with_body(body.clone());
6334
6335 let settings = Frame {
6340 typ: F_SETTINGS,
6341 flags: 0,
6342 stream_id: 0,
6343 payload: settings_payload(&[(S_INITIAL_WINDOW_SIZE, 100)]),
6344 };
6345 let mut conn = fake_conn_with_inbound(&[settings]);
6346 conn.peer.initial_window_size = 0;
6347
6348 let id = conn.open_stream().unwrap();
6349 assert_eq!(conn.streams.get(&id).unwrap().send_window.available, 0);
6350
6351 conn.send_request_on(id, &req).unwrap();
6352
6353 let frames = drain_wire_out(&conn);
6354 let data: Vec<&Frame> = frames.iter().filter(|f| f.typ == F_DATA).collect();
6355 assert_eq!(data.len(), 1, "after the delta the whole body fits");
6356 assert_eq!(data[0].payload, body);
6357 assert_eq!(data[0].flags & FLAG_END_STREAM, FLAG_END_STREAM);
6358 assert_eq!(conn.streams.get(&id).unwrap().send_window.available, 94);
6360 assert!(
6362 frames
6363 .iter()
6364 .any(|f| f.typ == F_SETTINGS && f.flags & FLAG_ACK != 0),
6365 "SETTINGS must be ACKed"
6366 );
6367 }
6368
6369 fn synth_rst(id: u32, code: u32) -> Frame {
6379 Frame {
6380 typ: F_RST_STREAM,
6381 flags: 0,
6382 stream_id: id,
6383 payload: code.to_be_bytes().to_vec(),
6384 }
6385 }
6386
6387 #[test]
6388 fn multiplex_two_requests_demuxes_interleaved_frames() {
6389 let inbound = vec![
6395 synth_status_200_headers(1, false),
6396 synth_status_200_headers(3, false),
6397 synth_data(1, b"one-", false),
6398 synth_data(3, b"THREE", true),
6399 synth_data(1, b"part", true),
6400 ];
6401 let mut conn = fake_conn_with_inbound(&inbound);
6402
6403 let reqs = vec![
6404 h2_get("https://example.com/a"),
6405 h2_get("https://example.com/b"),
6406 ];
6407 let results = conn.run_multiplexed(&reqs, &mut std::io::sink());
6408 assert_eq!(results.len(), 2);
6409
6410 let r0 = results[0].as_ref().expect("req 0 ok");
6411 let r1 = results[1].as_ref().expect("req 1 ok");
6412 assert_eq!(r0.status, 200);
6413 assert_eq!(r0.body, b"one-part", "stream 1 body");
6414 assert_eq!(r1.status, 200);
6415 assert_eq!(r1.body, b"THREE", "stream 3 body");
6416
6417 let header_ids: Vec<u32> = drain_wire_out(&conn)
6419 .into_iter()
6420 .filter(|f| f.typ == F_HEADERS)
6421 .map(|f| f.stream_id)
6422 .collect();
6423 assert_eq!(header_ids, vec![1, 3]);
6424 assert!(conn.streams.is_empty());
6426 }
6427
6428 #[test]
6429 fn multiplex_reversed_interleave_still_demuxes() {
6430 let inbound = vec![
6433 synth_status_200_headers(3, false),
6434 synth_data(3, b"bbb", true),
6435 synth_status_200_headers(1, false),
6436 synth_data(1, b"aaaa", true),
6437 ];
6438 let mut conn = fake_conn_with_inbound(&inbound);
6439 let reqs = vec![
6440 h2_get("https://example.com/a"),
6441 h2_get("https://example.com/b"),
6442 ];
6443 let results = conn.run_multiplexed(&reqs, &mut std::io::sink());
6444 assert_eq!(results[0].as_ref().unwrap().body, b"aaaa");
6445 assert_eq!(results[1].as_ref().unwrap().body, b"bbb");
6446 }
6447
6448 #[test]
6449 fn multiplex_queues_third_request_at_max_concurrent_two() {
6450 let inbound = vec![
6455 synth_status_200_headers(1, false),
6457 synth_data(1, b"first", true),
6458 synth_status_200_headers(3, false),
6460 synth_data(3, b"second", true),
6461 synth_status_200_headers(5, false),
6463 synth_data(5, b"third", true),
6464 ];
6465 let mut conn = fake_conn_with_inbound(&inbound);
6466 conn.peer.max_concurrent_streams = 2;
6467
6468 let reqs = vec![
6469 h2_get("https://example.com/1"),
6470 h2_get("https://example.com/2"),
6471 h2_get("https://example.com/3"),
6472 ];
6473 let results = conn.run_multiplexed(&reqs, &mut std::io::sink());
6474 assert_eq!(results.len(), 3);
6475 assert_eq!(results[0].as_ref().unwrap().body, b"first");
6476 assert_eq!(results[1].as_ref().unwrap().body, b"second");
6477 assert_eq!(results[2].as_ref().unwrap().body, b"third");
6478
6479 let header_ids: Vec<u32> = drain_wire_out(&conn)
6483 .into_iter()
6484 .filter(|f| f.typ == F_HEADERS)
6485 .map(|f| f.stream_id)
6486 .collect();
6487 assert_eq!(
6488 header_ids,
6489 vec![1, 3, 5],
6490 "stream 5 must be opened only after a slot freed"
6491 );
6492 }
6493
6494 #[test]
6495 fn multiplex_one_stream_rst_others_succeed() {
6496 let inbound = vec![
6500 synth_status_200_headers(3, false),
6501 synth_rst(1, 0x8), synth_data(3, b"alive", true),
6503 ];
6504 let mut conn = fake_conn_with_inbound(&inbound);
6505 let reqs = vec![
6506 h2_get("https://example.com/doomed"),
6507 h2_get("https://example.com/ok"),
6508 ];
6509 let results = conn.run_multiplexed(&reqs, &mut std::io::sink());
6510 assert_eq!(results.len(), 2);
6511 assert!(
6512 matches!(results[0], Err(Error::BadResponse(_))),
6513 "reset stream must yield an error, got {:?}",
6514 results[0]
6515 );
6516 let ok = results[1].as_ref().expect("stream 3 should succeed");
6517 assert_eq!(ok.body, b"alive");
6518 }
6519
6520 #[test]
6521 fn multiplex_flow_control_no_head_of_line_block() {
6522 let mut req1 = Request::new("POST", "https://example.com/u1").unwrap();
6529 req1.body = (0..10u8).collect();
6530 let mut req3 = Request::new("POST", "https://example.com/u3").unwrap();
6531 req3.body = (100..110u8).collect();
6532
6533 let inbound = vec![
6538 window_update_frame(1, 6), window_update_frame(3, 6), window_update_frame(0, 12), synth_status_200_headers(1, false),
6542 synth_data(1, b"r1", true),
6543 synth_status_200_headers(3, false),
6544 synth_data(3, b"r3", true),
6545 ];
6546 let mut conn = fake_conn_with_inbound(&inbound);
6547 conn.peer.initial_window_size = 4;
6550
6551 let reqs = vec![req1.clone(), req3.clone()];
6552 let results = conn.run_multiplexed(&reqs, &mut std::io::sink());
6553 assert_eq!(results.len(), 2);
6554 assert_eq!(results[0].as_ref().unwrap().body, b"r1");
6555 assert_eq!(results[1].as_ref().unwrap().body, b"r3");
6556
6557 let data: Vec<Frame> = drain_wire_out(&conn)
6561 .into_iter()
6562 .filter(|f| f.typ == F_DATA)
6563 .collect();
6564 let mut s1 = Vec::new();
6566 let mut s3 = Vec::new();
6567 for f in &data {
6568 if f.stream_id == 1 {
6569 s1.extend_from_slice(&f.payload);
6570 } else if f.stream_id == 3 {
6571 s3.extend_from_slice(&f.payload);
6572 }
6573 }
6574 assert_eq!(s1, req1.body);
6575 assert_eq!(s3, req3.body);
6576 let first_s1 = data.iter().find(|f| f.stream_id == 1).unwrap();
6578 assert_eq!(
6579 first_s1.payload.len(),
6580 4,
6581 "stream 1 first DATA capped to window"
6582 );
6583 let first_s3 = data.iter().find(|f| f.stream_id == 3).unwrap();
6584 assert_eq!(
6585 first_s3.payload.len(),
6586 4,
6587 "stream 3 first DATA capped to window"
6588 );
6589 }
6590
6591 #[test]
6592 fn multiplex_goaway_fails_high_streams_lower_completes() {
6593 let mut goaway_payload = Vec::new();
6598 goaway_payload.extend_from_slice(&3u32.to_be_bytes()); goaway_payload.extend_from_slice(&0u32.to_be_bytes()); let goaway = Frame {
6601 typ: F_GOAWAY,
6602 flags: 0,
6603 stream_id: 0,
6604 payload: goaway_payload,
6605 };
6606 let inbound = vec![
6607 synth_status_200_headers(1, false),
6608 synth_data(1, b"one", true),
6609 goaway,
6610 synth_status_200_headers(3, false),
6611 synth_data(3, b"three", true),
6612 ];
6613 let mut conn = fake_conn_with_inbound(&inbound);
6614 conn.peer.max_concurrent_streams = 3;
6615
6616 let reqs = vec![
6617 h2_get("https://example.com/1"),
6618 h2_get("https://example.com/3"),
6619 h2_get("https://example.com/5"),
6620 ];
6621 let results = conn.run_multiplexed(&reqs, &mut std::io::sink());
6622 assert_eq!(results.len(), 3);
6623 assert_eq!(
6624 results[0].as_ref().unwrap().body,
6625 b"one",
6626 "stream 1 completes"
6627 );
6628 assert_eq!(
6629 results[1].as_ref().unwrap().body,
6630 b"three",
6631 "stream 3 (<= last-stream-id) completes"
6632 );
6633 assert!(
6634 matches!(results[2], Err(Error::BadResponse(_))),
6635 "stream 5 (> last-stream-id) must be abandoned, got {:?}",
6636 results[2]
6637 );
6638 }
6639
6640 #[test]
6641 fn multiplex_verbose_trace_labels_streams() {
6642 let inbound = vec![
6645 synth_status_200_headers(1, false),
6646 synth_data(1, b"x", true),
6647 synth_status_200_headers(3, false),
6648 synth_data(3, b"y", true),
6649 ];
6650 let mut conn = fake_conn_with_inbound(&inbound);
6651 let reqs = vec![
6652 h2_get("https://example.com/a"),
6653 h2_get("https://example.com/b"),
6654 ];
6655 let mut trace: Vec<u8> = Vec::new();
6656 let _ = conn.run_multiplexed(&reqs, &mut trace);
6657 let t = String::from_utf8(trace).unwrap();
6658 assert!(t.contains("> [stream 1] GET /a HTTP/2"), "trace:\n{t}");
6659 assert!(t.contains("> [stream 3] GET /b HTTP/2"), "trace:\n{t}");
6660 assert!(t.contains("< [stream 1] HTTP/2 200"), "trace:\n{t}");
6661 assert!(t.contains("< [stream 3] HTTP/2 200"), "trace:\n{t}");
6662 }
6663
6664 #[test]
6665 fn send_multiplexed_empty_returns_empty() {
6666 let mut sink = std::io::sink();
6667 let out = send_multiplexed(Vec::new(), &mut sink);
6668 assert!(out.is_empty());
6669 }
6670}