1use std::cmp;
2use std::fmt;
3use std::io::{self, IoSlice};
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use crate::rt::{Read, ReadBuf, Write};
8use bytes::{Buf, BufMut, Bytes, BytesMut};
9use futures_core::ready;
10
11use super::{Http1Transaction, ParseContext, ParsedMessage};
12use crate::common::buf::BufList;
13
14pub(crate) const INIT_BUFFER_SIZE: usize = 8192;
16
17pub(crate) const MINIMUM_MAX_BUFFER_SIZE: usize = INIT_BUFFER_SIZE;
19
20pub(crate) const DEFAULT_MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
24
25const MAX_BUF_LIST_BUFFERS: usize = 16;
31
32pub(crate) struct Buffered<T, B> {
33 flush_pipeline: bool,
34 io: T,
35 partial_len: Option<usize>,
36 read_blocked: bool,
37 read_buf: BytesMut,
38 read_buf_strategy: ReadStrategy,
39 write_buf: WriteBuf<B>,
40}
41
42impl<T, B> fmt::Debug for Buffered<T, B>
43where
44 B: Buf,
45{
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 f.debug_struct("Buffered")
48 .field("read_buf", &self.read_buf)
49 .field("write_buf", &self.write_buf)
50 .finish()
51 }
52}
53
54impl<T, B> Buffered<T, B>
55where
56 T: Read + Write + Unpin,
57 B: Buf,
58{
59 pub(crate) fn new(io: T) -> Buffered<T, B> {
60 let strategy = if io.is_write_vectored() {
61 WriteStrategy::Queue
62 } else {
63 WriteStrategy::Flatten
64 };
65 let write_buf = WriteBuf::new(strategy);
66 Buffered {
67 flush_pipeline: false,
68 io,
69 partial_len: None,
70 read_blocked: false,
71 read_buf: BytesMut::with_capacity(0),
72 read_buf_strategy: ReadStrategy::default(),
73 write_buf,
74 }
75 }
76
77 #[cfg(feature = "server")]
78 pub(crate) fn set_flush_pipeline(&mut self, enabled: bool) {
79 debug_assert!(!self.write_buf.has_remaining());
80 self.flush_pipeline = enabled;
81 if enabled {
82 self.set_write_strategy_flatten();
83 }
84 }
85
86 pub(crate) fn set_max_buf_size(&mut self, max: usize) {
87 assert!(
88 max >= MINIMUM_MAX_BUFFER_SIZE,
89 "The max_buf_size cannot be smaller than {MINIMUM_MAX_BUFFER_SIZE}.",
90 );
91 self.read_buf_strategy = ReadStrategy::with_max(max);
92 self.write_buf.max_buf_size = max;
93 }
94
95 #[cfg(feature = "client")]
96 pub(crate) fn set_read_buf_exact_size(&mut self, sz: usize) {
97 self.read_buf_strategy = ReadStrategy::Exact(sz);
98 }
99
100 pub(crate) fn set_write_strategy_flatten(&mut self) {
101 debug_assert_eq!(self.write_buf.queue.bufs_cnt(), 0);
104 self.write_buf.set_strategy(WriteStrategy::Flatten);
105 }
106
107 pub(crate) fn set_write_strategy_queue(&mut self) {
108 debug_assert_eq!(self.write_buf.queue.bufs_cnt(), 0);
111 self.write_buf.set_strategy(WriteStrategy::Queue);
112 }
113
114 pub(crate) fn read_buf(&self) -> &[u8] {
115 self.read_buf.as_ref()
116 }
117
118 #[cfg(test)]
119 #[cfg(feature = "nightly")]
120 pub(super) fn read_buf_mut(&mut self) -> &mut BytesMut {
121 &mut self.read_buf
122 }
123
124 fn read_buf_remaining_mut(&self) -> usize {
127 self.read_buf.capacity() - self.read_buf.len()
128 }
129
130 pub(crate) fn can_headers_buf(&self) -> bool {
136 !self.write_buf.queue.has_remaining()
137 }
138
139 pub(crate) fn headers_buf(&mut self) -> &mut Vec<u8> {
140 let buf = self.write_buf.headers_mut();
141 &mut buf.bytes
142 }
143
144 pub(super) fn write_buf(&mut self) -> &mut WriteBuf<B> {
145 &mut self.write_buf
146 }
147
148 pub(crate) fn buffer<BB: Buf + Into<B>>(&mut self, buf: BB) {
149 self.write_buf.buffer(buf);
150 }
151
152 pub(crate) fn has_buffered_write(&self) -> bool {
154 self.write_buf.remaining() > 0
155 }
156
157 pub(crate) fn can_buffer(&self) -> bool {
158 self.flush_pipeline || self.write_buf.can_buffer()
159 }
160
161 pub(crate) fn consume_leading_lines(&mut self) {
162 if !self.read_buf.is_empty() {
163 let mut i = 0;
164 while i < self.read_buf.len() {
165 match self.read_buf[i] {
166 b'\r' | b'\n' => i += 1,
167 _ => break,
168 }
169 }
170 self.read_buf.advance(i);
171 }
172 }
173
174 pub(super) fn parse<S>(
175 &mut self,
176 cx: &mut Context<'_>,
177 parse_ctx: ParseContext<'_>,
178 ) -> Poll<crate::Result<ParsedMessage<S::Incoming>>>
179 where
180 S: Http1Transaction,
181 {
182 loop {
183 if let Some(msg) = super::role::parse_headers::<S>(
184 &mut self.read_buf,
185 self.partial_len,
186 ParseContext {
187 cached_headers: parse_ctx.cached_headers,
188 req_method: parse_ctx.req_method,
189 h1_parser_config: parse_ctx.h1_parser_config.clone(),
190 h1_max_headers: parse_ctx.h1_max_headers,
191 preserve_header_case: parse_ctx.preserve_header_case,
192 #[cfg(feature = "ffi")]
193 preserve_header_order: parse_ctx.preserve_header_order,
194 h09_responses: parse_ctx.h09_responses,
195 #[cfg(feature = "client")]
196 on_informational: parse_ctx.on_informational,
197 },
198 )? {
199 debug!("parsed {} headers", msg.head.headers.len());
200 self.partial_len = None;
201 return Poll::Ready(Ok(msg));
202 } else {
203 let max = self.read_buf_strategy.max();
204 let curr_len = self.read_buf.len();
205 if curr_len >= max {
206 debug!("max_buf_size ({}) reached, closing", max);
207 return Poll::Ready(Err(crate::Error::new_too_large()));
208 }
209 if curr_len > 0 {
210 trace!("partial headers; {} bytes so far", curr_len);
211 self.partial_len = Some(curr_len);
212 } else {
213 self.partial_len = None;
215 }
216 }
217 if ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? == 0 {
218 trace!("parse eof");
219 return Poll::Ready(Err(crate::Error::new_incomplete()));
220 }
221 }
222 }
223
224 pub(crate) fn poll_read_from_io(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
225 self.read_blocked = false;
226 let next = cmp::min(
229 self.read_buf_strategy.next(),
230 self.read_buf_strategy
231 .max()
232 .saturating_sub(self.read_buf.len()),
233 );
234 if self.read_buf_remaining_mut() < next {
235 self.read_buf.reserve(next);
236 }
237
238 let dst = unsafe { self.read_buf.chunk_mut().as_uninit_slice_mut() };
241 let mut buf = ReadBuf::uninit(dst);
242 match Pin::new(&mut self.io).poll_read(cx, buf.unfilled()) {
243 Poll::Ready(Ok(_)) => {
244 let n = buf.filled().len();
245 trace!("received {} bytes", n);
246 unsafe {
250 self.read_buf.advance_mut(n);
251 }
252 self.read_buf_strategy.record(n);
253 Poll::Ready(Ok(n))
254 }
255 Poll::Pending => {
256 self.read_blocked = true;
257 Poll::Pending
258 }
259 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
260 }
261 }
262
263 pub(crate) fn into_inner(self) -> (T, Bytes) {
264 (self.io, self.read_buf.freeze())
265 }
266
267 pub(crate) fn is_read_blocked(&self) -> bool {
268 self.read_blocked
269 }
270
271 pub(crate) fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
272 if self.flush_pipeline && !self.read_buf.is_empty() {
273 Poll::Ready(Ok(()))
274 } else if self.write_buf.remaining() == 0 {
275 Pin::new(&mut self.io).poll_flush(cx)
276 } else {
277 if let WriteStrategy::Flatten = self.write_buf.strategy {
278 return self.poll_flush_flattened(cx);
279 }
280
281 const MAX_WRITEV_BUFS: usize = 64;
282 loop {
283 let n = {
284 let mut iovs = [IoSlice::new(&[]); MAX_WRITEV_BUFS];
285 let len = self.write_buf.chunks_vectored(&mut iovs);
286 ready!(Pin::new(&mut self.io).poll_write_vectored(cx, &iovs[..len]))?
287 };
288 self.write_buf.advance(n);
292 debug!("flushed {} bytes", n);
293 if self.write_buf.remaining() == 0 {
294 break;
295 } else if n == 0 {
296 trace!(
297 "write returned zero, but {} bytes remaining",
298 self.write_buf.remaining()
299 );
300 return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
301 }
302 }
303 Pin::new(&mut self.io).poll_flush(cx)
304 }
305 }
306
307 fn poll_flush_flattened(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
312 loop {
313 let n = ready!(Pin::new(&mut self.io).poll_write(cx, self.write_buf.headers.chunk()))?;
314 debug!("flushed {} bytes", n);
315 self.write_buf.headers.advance(n);
316 if self.write_buf.headers.remaining() == 0 {
317 self.write_buf.headers.reset();
318 break;
319 } else if n == 0 {
320 trace!(
321 "write returned zero, but {} bytes remaining",
322 self.write_buf.remaining()
323 );
324 return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
325 }
326 }
327 Pin::new(&mut self.io).poll_flush(cx)
328 }
329
330 pub(crate) fn poll_shutdown(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
331 ready!(self.poll_flush(cx))?;
332 Pin::new(&mut self.io).poll_shutdown(cx)
333 }
334
335 #[cfg(test)]
336 fn flush(&mut self) -> impl std::future::Future<Output = io::Result<()>> + '_ {
337 futures_util::future::poll_fn(move |cx| self.poll_flush(cx))
338 }
339}
340
341impl<T: Unpin, B> Unpin for Buffered<T, B> {}
343
344pub(crate) trait MemRead {
346 fn read_mem(&mut self, cx: &mut Context<'_>, len: usize) -> Poll<io::Result<Bytes>>;
347}
348
349impl<T, B> MemRead for Buffered<T, B>
350where
351 T: Read + Write + Unpin,
352 B: Buf,
353{
354 fn read_mem(&mut self, cx: &mut Context<'_>, len: usize) -> Poll<io::Result<Bytes>> {
355 if !self.read_buf.is_empty() {
356 let n = std::cmp::min(len, self.read_buf.len());
357 Poll::Ready(Ok(self.read_buf.split_to(n).freeze()))
358 } else {
359 let n = ready!(self.poll_read_from_io(cx))?;
360 Poll::Ready(Ok(self.read_buf.split_to(::std::cmp::min(len, n)).freeze()))
361 }
362 }
363}
364
365#[derive(Clone, Copy, Debug)]
366enum ReadStrategy {
367 Adaptive {
368 decrease_now: bool,
369 next: usize,
370 max: usize,
371 },
372 #[cfg(feature = "client")]
373 Exact(usize),
374}
375
376impl ReadStrategy {
377 fn with_max(max: usize) -> ReadStrategy {
378 ReadStrategy::Adaptive {
379 decrease_now: false,
380 next: INIT_BUFFER_SIZE,
381 max,
382 }
383 }
384
385 fn next(&self) -> usize {
386 match *self {
387 ReadStrategy::Adaptive { next, .. } => next,
388 #[cfg(feature = "client")]
389 ReadStrategy::Exact(exact) => exact,
390 }
391 }
392
393 fn max(&self) -> usize {
394 match *self {
395 ReadStrategy::Adaptive { max, .. } => max,
396 #[cfg(feature = "client")]
397 ReadStrategy::Exact(exact) => exact,
398 }
399 }
400
401 fn record(&mut self, bytes_read: usize) {
402 match self {
403 ReadStrategy::Adaptive {
404 decrease_now,
405 next,
406 max,
407 ..
408 } => {
409 if bytes_read >= *next {
410 *next = cmp::min(incr_power_of_two(*next), *max);
411 *decrease_now = false;
412 } else {
413 let decr_to = prev_power_of_two(*next);
414 if bytes_read < decr_to {
415 if *decrease_now {
416 *next = cmp::max(decr_to, INIT_BUFFER_SIZE);
417 *decrease_now = false;
418 } else {
419 *decrease_now = true;
421 }
422 } else {
423 *decrease_now = false;
427 }
428 }
429 }
430 #[cfg(feature = "client")]
431 ReadStrategy::Exact(_) => (),
432 }
433 }
434}
435
436fn incr_power_of_two(n: usize) -> usize {
437 n.saturating_mul(2)
438}
439
440fn prev_power_of_two(n: usize) -> usize {
441 debug_assert!(n >= 4);
444 (usize::MAX >> (n.leading_zeros() + 2)) + 1
445}
446
447impl Default for ReadStrategy {
448 fn default() -> ReadStrategy {
449 ReadStrategy::with_max(DEFAULT_MAX_BUFFER_SIZE)
450 }
451}
452
453#[derive(Clone)]
454pub(crate) struct Cursor<T> {
455 bytes: T,
456 pos: usize,
457}
458
459impl<T: AsRef<[u8]>> Cursor<T> {
460 #[inline]
461 pub(crate) fn new(bytes: T) -> Cursor<T> {
462 Cursor { bytes, pos: 0 }
463 }
464}
465
466impl Cursor<Vec<u8>> {
467 fn maybe_unshift(&mut self, additional: usize) {
471 if self.pos == 0 {
472 return;
474 }
475
476 if self.bytes.capacity() - self.bytes.len() >= additional {
477 return;
479 }
480
481 self.bytes.drain(0..self.pos);
482 self.pos = 0;
483 }
484
485 fn reset(&mut self) {
486 self.pos = 0;
487 self.bytes.clear();
488 }
489}
490
491impl<T: AsRef<[u8]>> fmt::Debug for Cursor<T> {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 f.debug_struct("Cursor")
494 .field("pos", &self.pos)
495 .field("len", &self.bytes.as_ref().len())
496 .finish()
497 }
498}
499
500impl<T: AsRef<[u8]>> Buf for Cursor<T> {
501 #[inline]
502 fn remaining(&self) -> usize {
503 self.bytes.as_ref().len() - self.pos
504 }
505
506 #[inline]
507 fn chunk(&self) -> &[u8] {
508 &self.bytes.as_ref()[self.pos..]
509 }
510
511 #[inline]
512 fn advance(&mut self, cnt: usize) {
513 debug_assert!(self.pos + cnt <= self.bytes.as_ref().len());
514 self.pos += cnt;
515 }
516}
517
518pub(super) struct WriteBuf<B> {
520 headers: Cursor<Vec<u8>>,
522 max_buf_size: usize,
523 queue: BufList<B>,
525 strategy: WriteStrategy,
526}
527
528impl<B: Buf> WriteBuf<B> {
529 fn new(strategy: WriteStrategy) -> WriteBuf<B> {
530 WriteBuf {
531 headers: Cursor::new(Vec::with_capacity(INIT_BUFFER_SIZE)),
532 max_buf_size: DEFAULT_MAX_BUFFER_SIZE,
533 queue: BufList::new(),
534 strategy,
535 }
536 }
537}
538
539impl<B> WriteBuf<B>
540where
541 B: Buf,
542{
543 fn set_strategy(&mut self, strategy: WriteStrategy) {
544 self.strategy = strategy;
545 }
546
547 pub(super) fn buffer<BB: Buf + Into<B>>(&mut self, mut buf: BB) {
548 debug_assert!(buf.has_remaining());
549 match self.strategy {
550 WriteStrategy::Flatten => {
551 let head = self.headers_mut();
552
553 head.maybe_unshift(buf.remaining());
554 trace!(
555 self.len = head.remaining(),
556 buf.len = buf.remaining(),
557 "buffer.flatten"
558 );
559 loop {
562 let adv = {
563 let slice = buf.chunk();
564 if slice.is_empty() {
565 return;
566 }
567 head.bytes.extend_from_slice(slice);
568 slice.len()
569 };
570 buf.advance(adv);
571 }
572 }
573 WriteStrategy::Queue => {
574 trace!(
575 self.len = self.remaining(),
576 buf.len = buf.remaining(),
577 "buffer.queue"
578 );
579 self.queue.push(buf.into());
580 }
581 }
582 }
583
584 fn can_buffer(&self) -> bool {
585 match self.strategy {
586 WriteStrategy::Flatten => self.remaining() < self.max_buf_size,
587 WriteStrategy::Queue => {
588 self.queue.bufs_cnt() < MAX_BUF_LIST_BUFFERS && self.remaining() < self.max_buf_size
589 }
590 }
591 }
592
593 fn headers_mut(&mut self) -> &mut Cursor<Vec<u8>> {
594 debug_assert!(!self.queue.has_remaining());
595 &mut self.headers
596 }
597}
598
599impl<B: Buf> fmt::Debug for WriteBuf<B> {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 f.debug_struct("WriteBuf")
602 .field("remaining", &self.remaining())
603 .field("strategy", &self.strategy)
604 .finish()
605 }
606}
607
608impl<B: Buf> Buf for WriteBuf<B> {
609 #[inline]
610 fn remaining(&self) -> usize {
611 self.headers.remaining() + self.queue.remaining()
612 }
613
614 #[inline]
615 fn chunk(&self) -> &[u8] {
616 let headers = self.headers.chunk();
617 if !headers.is_empty() {
618 headers
619 } else {
620 self.queue.chunk()
621 }
622 }
623
624 #[inline]
625 fn advance(&mut self, cnt: usize) {
626 let hrem = self.headers.remaining();
627
628 match hrem.cmp(&cnt) {
629 cmp::Ordering::Equal => self.headers.reset(),
630 cmp::Ordering::Greater => self.headers.advance(cnt),
631 cmp::Ordering::Less => {
632 let qcnt = cnt - hrem;
633 self.headers.reset();
634 self.queue.advance(qcnt);
635 }
636 }
637 }
638
639 #[inline]
640 fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
641 let n = self.headers.chunks_vectored(dst);
642 self.queue.chunks_vectored(&mut dst[n..]) + n
643 }
644}
645
646#[derive(Debug)]
647enum WriteStrategy {
648 Flatten,
649 Queue,
650}
651
652#[cfg(test)]
653mod tests {
654 use super::*;
655 use crate::common::io::Compat;
656 use std::time::Duration;
657
658 use tokio_test::io::Builder as Mock;
659
660 #[tokio::test]
661 #[ignore]
662 async fn iobuf_write_empty_slice() {
663 }
680
681 #[cfg(not(miri))]
682 #[tokio::test]
683 async fn parse_reads_until_blocked() {
684 use crate::proto::h1::ClientTransaction;
685
686 let _ = pretty_env_logger::try_init();
687 let mock = Mock::new()
688 .read(b"HTTP/1.1 200 OK\r\n")
690 .read(b"Server: hyper\r\n")
691 .wait(Duration::from_secs(1))
693 .build();
694
695 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
696
697 futures_util::future::poll_fn(|cx| {
700 let parse_ctx = ParseContext {
701 cached_headers: &mut None,
702 req_method: &mut None,
703 h1_parser_config: Default::default(),
704 h1_max_headers: None,
705 preserve_header_case: false,
706 #[cfg(feature = "ffi")]
707 preserve_header_order: false,
708 h09_responses: false,
709 #[cfg(feature = "client")]
710 on_informational: &mut None,
711 };
712 assert!(buffered
713 .parse::<ClientTransaction>(cx, parse_ctx)
714 .is_pending());
715 Poll::Ready(())
716 })
717 .await;
718
719 assert_eq!(
720 buffered.read_buf,
721 b"HTTP/1.1 200 OK\r\nServer: hyper\r\n"[..]
722 );
723 }
724
725 #[test]
726 fn read_strategy_adaptive_increments() {
727 let mut strategy = ReadStrategy::default();
728 assert_eq!(strategy.next(), 8192);
729
730 strategy.record(8192);
732 assert_eq!(strategy.next(), 16384);
733
734 strategy.record(16384);
735 assert_eq!(strategy.next(), 32768);
736
737 strategy.record(usize::MAX);
739 assert_eq!(strategy.next(), 65536);
740
741 let max = strategy.max();
742 while strategy.next() < max {
743 strategy.record(max);
744 }
745
746 assert_eq!(strategy.next(), max, "never goes over max");
747 strategy.record(max + 1);
748 assert_eq!(strategy.next(), max, "never goes over max");
749 }
750
751 #[test]
752 fn read_strategy_adaptive_decrements() {
753 let mut strategy = ReadStrategy::default();
754 strategy.record(8192);
755 assert_eq!(strategy.next(), 16384);
756
757 strategy.record(1);
758 assert_eq!(
759 strategy.next(),
760 16384,
761 "first smaller record doesn't decrement yet"
762 );
763 strategy.record(8192);
764 assert_eq!(strategy.next(), 16384, "record was with range");
765
766 strategy.record(1);
767 assert_eq!(
768 strategy.next(),
769 16384,
770 "in-range record should make this the 'first' again"
771 );
772
773 strategy.record(1);
774 assert_eq!(strategy.next(), 8192, "second smaller record decrements");
775
776 strategy.record(1);
777 assert_eq!(strategy.next(), 8192, "first doesn't decrement");
778 strategy.record(1);
779 assert_eq!(strategy.next(), 8192, "doesn't decrement under minimum");
780 }
781
782 #[test]
783 fn read_strategy_adaptive_stays_the_same() {
784 let mut strategy = ReadStrategy::default();
785 strategy.record(8192);
786 assert_eq!(strategy.next(), 16384);
787
788 strategy.record(8193);
789 assert_eq!(
790 strategy.next(),
791 16384,
792 "first smaller record doesn't decrement yet"
793 );
794
795 strategy.record(8193);
796 assert_eq!(
797 strategy.next(),
798 16384,
799 "with current step does not decrement"
800 );
801 }
802
803 #[test]
804 fn read_strategy_adaptive_max_fuzz() {
805 fn fuzz(max: usize) {
806 let mut strategy = ReadStrategy::with_max(max);
807 while strategy.next() < max {
808 strategy.record(usize::MAX);
809 }
810 let mut next = strategy.next();
811 while next > 8192 {
812 strategy.record(1);
813 strategy.record(1);
814 next = strategy.next();
815 assert!(
816 next.is_power_of_two(),
817 "decrement should be powers of two: {} (max = {})",
818 next,
819 max,
820 );
821 }
822 }
823
824 let mut max = 8192;
825 while max < usize::MAX {
826 fuzz(max);
827 max = (max / 2).saturating_mul(3);
828 }
829 fuzz(usize::MAX);
830 }
831
832 #[test]
833 #[should_panic]
834 #[cfg(debug_assertions)] fn write_buf_requires_non_empty_bufs() {
836 let mock = Mock::new().build();
837 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
838
839 buffered.buffer(Cursor::new(Vec::new()));
840 }
841
842 #[cfg(not(miri))]
866 #[tokio::test]
867 async fn write_buf_flatten() {
868 let _ = pretty_env_logger::try_init();
869
870 let mock = Mock::new().write(b"hello world, it's hyper!").build();
871
872 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
873 buffered.write_buf.set_strategy(WriteStrategy::Flatten);
874
875 buffered.headers_buf().extend(b"hello ");
876 buffered.buffer(Cursor::new(b"world, ".to_vec()));
877 buffered.buffer(Cursor::new(b"it's ".to_vec()));
878 buffered.buffer(Cursor::new(b"hyper!".to_vec()));
879 assert_eq!(buffered.write_buf.queue.bufs_cnt(), 0);
880
881 buffered.flush().await.expect("flush");
882 }
883
884 #[test]
885 fn write_buf_flatten_partially_flushed() {
886 let _ = pretty_env_logger::try_init();
887
888 let b = |s: &str| Cursor::new(s.as_bytes().to_vec());
889
890 let mut write_buf = WriteBuf::<Cursor<Vec<u8>>>::new(WriteStrategy::Flatten);
891
892 write_buf.buffer(b("hello "));
893 write_buf.buffer(b("world, "));
894
895 assert_eq!(write_buf.chunk(), b"hello world, ");
896
897 write_buf.advance(11);
899
900 assert_eq!(write_buf.chunk(), b", ");
901 assert_eq!(write_buf.headers.pos, 11);
902 assert_eq!(write_buf.headers.bytes.capacity(), INIT_BUFFER_SIZE);
903
904 write_buf.buffer(b("it's hyper!"));
906
907 assert_eq!(write_buf.chunk(), b", it's hyper!");
908 assert_eq!(write_buf.headers.pos, 11);
909
910 let rem1 = write_buf.remaining();
911 let cap = write_buf.headers.bytes.capacity();
912
913 write_buf.buffer(Cursor::new(vec![b'X'; cap]));
915 assert_eq!(write_buf.remaining(), cap + rem1);
916 assert_eq!(write_buf.headers.pos, 0);
917 }
918
919 #[cfg(not(miri))]
920 #[tokio::test]
921 async fn write_buf_queue_disable_auto() {
922 let _ = pretty_env_logger::try_init();
923
924 let mock = Mock::new()
925 .write(b"hello ")
926 .write(b"world, ")
927 .write(b"it's ")
928 .write(b"hyper!")
929 .build();
930
931 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
932 buffered.write_buf.set_strategy(WriteStrategy::Queue);
933
934 buffered.headers_buf().extend(b"hello ");
938 buffered.buffer(Cursor::new(b"world, ".to_vec()));
939 buffered.buffer(Cursor::new(b"it's ".to_vec()));
940 buffered.buffer(Cursor::new(b"hyper!".to_vec()));
941 assert_eq!(buffered.write_buf.queue.bufs_cnt(), 3);
942
943 buffered.flush().await.expect("flush");
944
945 assert_eq!(buffered.write_buf.queue.bufs_cnt(), 0);
946 }
947
948 }