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_non_exhaustive()
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 h1_max_header_size: parse_ctx.h1_max_header_size,
192 preserve_header_case: parse_ctx.preserve_header_case,
193 #[cfg(feature = "ffi")]
194 preserve_header_order: parse_ctx.preserve_header_order,
195 h09_responses: parse_ctx.h09_responses,
196 #[cfg(feature = "client")]
197 on_informational: parse_ctx.on_informational,
198 },
199 )? {
200 debug!("parsed {} headers", msg.head.headers.len());
201 self.partial_len = None;
202 return Poll::Ready(Ok(msg));
203 } else {
204 let curr_len = self.read_buf.len();
205 if let Some(max_header_size) = parse_ctx.h1_max_header_size {
206 if curr_len >= max_header_size {
207 debug!("max_header_size ({}) reached, closing", max_header_size);
208 return Poll::Ready(Err(crate::Error::new_too_large()));
209 }
210 }
211 let max = self.read_buf_strategy.max();
212 if curr_len >= max {
213 debug!("max_buf_size ({}) reached, closing", max);
214 return Poll::Ready(Err(crate::Error::new_too_large()));
215 }
216 if curr_len > 0 {
217 trace!("partial headers; {} bytes so far", curr_len);
218 self.partial_len = Some(curr_len);
219 } else {
220 self.partial_len = None;
222 }
223 }
224 if ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? == 0 {
225 trace!("parse eof");
226 return Poll::Ready(Err(crate::Error::new_incomplete()));
227 }
228 }
229 }
230
231 pub(crate) fn poll_read_from_io(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
232 self.read_blocked = false;
233 let next = cmp::min(
236 self.read_buf_strategy.next(),
237 self.read_buf_strategy
238 .max()
239 .saturating_sub(self.read_buf.len()),
240 );
241 if self.read_buf_remaining_mut() < next {
242 self.read_buf.reserve(next);
243 }
244
245 let dst = unsafe { self.read_buf.chunk_mut().as_uninit_slice_mut() };
248 let mut buf = ReadBuf::uninit(dst);
249 match Pin::new(&mut self.io).poll_read(cx, buf.unfilled()) {
250 Poll::Ready(Ok(())) => {
251 let n = buf.filled().len();
252 trace!("received {} bytes", n);
253 unsafe {
257 self.read_buf.advance_mut(n);
258 }
259 self.read_buf_strategy.record(n);
260 Poll::Ready(Ok(n))
261 }
262 Poll::Pending => {
263 self.read_blocked = true;
264 Poll::Pending
265 }
266 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
267 }
268 }
269
270 pub(crate) fn into_inner(self) -> (T, Bytes) {
271 (self.io, self.read_buf.freeze())
272 }
273
274 pub(crate) fn is_read_blocked(&self) -> bool {
275 self.read_blocked
276 }
277
278 pub(crate) fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
279 if self.flush_pipeline && !self.read_buf.is_empty() {
280 Poll::Ready(Ok(()))
281 } else if self.write_buf.remaining() == 0 {
282 Pin::new(&mut self.io).poll_flush(cx)
283 } else {
284 if let WriteStrategy::Flatten = self.write_buf.strategy {
285 return self.poll_flush_flattened(cx);
286 }
287
288 const MAX_WRITEV_BUFS: usize = 64;
289 loop {
290 let n = {
291 let mut iovs = [IoSlice::new(&[]); MAX_WRITEV_BUFS];
292 let len = self.write_buf.chunks_vectored(&mut iovs);
293 ready!(Pin::new(&mut self.io).poll_write_vectored(cx, &iovs[..len]))?
294 };
295 self.write_buf.advance(n);
299 debug!("flushed {} bytes", n);
300 if self.write_buf.remaining() == 0 {
301 break;
302 } else if n == 0 {
303 trace!(
304 "write returned zero, but {} bytes remaining",
305 self.write_buf.remaining()
306 );
307 return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
308 }
309 }
310 Pin::new(&mut self.io).poll_flush(cx)
311 }
312 }
313
314 fn poll_flush_flattened(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
319 loop {
320 let n = ready!(Pin::new(&mut self.io).poll_write(cx, self.write_buf.headers.chunk()))?;
321 debug!("flushed {} bytes", n);
322 self.write_buf.headers.advance(n);
323 if self.write_buf.headers.remaining() == 0 {
324 self.write_buf.headers.reset();
325 break;
326 } else if n == 0 {
327 trace!(
328 "write returned zero, but {} bytes remaining",
329 self.write_buf.remaining()
330 );
331 return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
332 }
333 }
334 Pin::new(&mut self.io).poll_flush(cx)
335 }
336
337 pub(crate) fn poll_shutdown(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
338 ready!(self.poll_flush(cx))?;
339 Pin::new(&mut self.io).poll_shutdown(cx)
340 }
341
342 #[cfg(test)]
343 fn flush(&mut self) -> impl std::future::Future<Output = io::Result<()>> + '_ {
344 futures_util::future::poll_fn(move |cx| self.poll_flush(cx))
345 }
346}
347
348impl<T: Unpin, B> Unpin for Buffered<T, B> {}
350
351pub(crate) trait MemRead {
353 fn read_mem(&mut self, cx: &mut Context<'_>, len: usize) -> Poll<io::Result<Bytes>>;
354}
355
356impl<T, B> MemRead for Buffered<T, B>
357where
358 T: Read + Write + Unpin,
359 B: Buf,
360{
361 fn read_mem(&mut self, cx: &mut Context<'_>, len: usize) -> Poll<io::Result<Bytes>> {
362 if !self.read_buf.is_empty() {
363 let n = std::cmp::min(len, self.read_buf.len());
364 Poll::Ready(Ok(self.read_buf.split_to(n).freeze()))
365 } else {
366 let n = ready!(self.poll_read_from_io(cx))?;
367 Poll::Ready(Ok(self.read_buf.split_to(::std::cmp::min(len, n)).freeze()))
368 }
369 }
370}
371
372#[derive(Clone, Copy, Debug)]
373enum ReadStrategy {
374 Adaptive {
375 decrease_now: bool,
376 next: usize,
377 max: usize,
378 },
379 #[cfg(feature = "client")]
380 Exact(usize),
381}
382
383impl ReadStrategy {
384 fn with_max(max: usize) -> ReadStrategy {
385 ReadStrategy::Adaptive {
386 decrease_now: false,
387 next: INIT_BUFFER_SIZE,
388 max,
389 }
390 }
391
392 fn next(&self) -> usize {
393 match *self {
394 ReadStrategy::Adaptive { next, .. } => next,
395 #[cfg(feature = "client")]
396 ReadStrategy::Exact(exact) => exact,
397 }
398 }
399
400 fn max(&self) -> usize {
401 match *self {
402 ReadStrategy::Adaptive { max, .. } => max,
403 #[cfg(feature = "client")]
404 ReadStrategy::Exact(exact) => exact,
405 }
406 }
407
408 fn record(&mut self, bytes_read: usize) {
409 match self {
410 ReadStrategy::Adaptive {
411 decrease_now,
412 next,
413 max,
414 ..
415 } => {
416 if bytes_read >= *next {
417 *next = cmp::min(incr_power_of_two(*next), *max);
418 *decrease_now = false;
419 } else {
420 let decr_to = prev_power_of_two(*next);
421 if bytes_read < decr_to {
422 if *decrease_now {
423 *next = cmp::max(decr_to, INIT_BUFFER_SIZE);
424 *decrease_now = false;
425 } else {
426 *decrease_now = true;
428 }
429 } else {
430 *decrease_now = false;
434 }
435 }
436 }
437 #[cfg(feature = "client")]
438 ReadStrategy::Exact(_) => (),
439 }
440 }
441}
442
443fn incr_power_of_two(n: usize) -> usize {
444 n.saturating_mul(2)
445}
446
447fn prev_power_of_two(n: usize) -> usize {
448 debug_assert!(n >= 4);
451 (usize::MAX >> (n.leading_zeros() + 2)) + 1
452}
453
454impl Default for ReadStrategy {
455 fn default() -> ReadStrategy {
456 ReadStrategy::with_max(DEFAULT_MAX_BUFFER_SIZE)
457 }
458}
459
460#[derive(Clone)]
461pub(crate) struct Cursor<T> {
462 bytes: T,
463 pos: usize,
464}
465
466impl<T: AsRef<[u8]>> Cursor<T> {
467 #[inline]
468 pub(crate) fn new(bytes: T) -> Cursor<T> {
469 Cursor { bytes, pos: 0 }
470 }
471}
472
473impl Cursor<Vec<u8>> {
474 fn maybe_unshift(&mut self, additional: usize) {
478 if self.pos == 0 {
479 return;
481 }
482
483 if self.bytes.capacity() - self.bytes.len() >= additional {
484 return;
486 }
487
488 self.bytes.drain(0..self.pos);
489 self.pos = 0;
490 }
491
492 fn reset(&mut self) {
493 self.pos = 0;
494 self.bytes.clear();
495 }
496}
497
498impl<T: AsRef<[u8]>> fmt::Debug for Cursor<T> {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 f.debug_struct("Cursor")
501 .field("pos", &self.pos)
502 .field("len", &self.bytes.as_ref().len())
503 .finish()
504 }
505}
506
507impl<T: AsRef<[u8]>> Buf for Cursor<T> {
508 #[inline]
509 fn remaining(&self) -> usize {
510 self.bytes.as_ref().len() - self.pos
511 }
512
513 #[inline]
514 fn chunk(&self) -> &[u8] {
515 &self.bytes.as_ref()[self.pos..]
516 }
517
518 #[inline]
519 fn advance(&mut self, cnt: usize) {
520 debug_assert!(self.pos + cnt <= self.bytes.as_ref().len());
521 self.pos += cnt;
522 }
523}
524
525pub(super) struct WriteBuf<B> {
527 headers: Cursor<Vec<u8>>,
529 max_buf_size: usize,
530 queue: BufList<B>,
532 strategy: WriteStrategy,
533}
534
535impl<B: Buf> WriteBuf<B> {
536 fn new(strategy: WriteStrategy) -> WriteBuf<B> {
537 WriteBuf {
538 headers: Cursor::new(Vec::with_capacity(INIT_BUFFER_SIZE)),
539 max_buf_size: DEFAULT_MAX_BUFFER_SIZE,
540 queue: BufList::new(),
541 strategy,
542 }
543 }
544
545 fn set_strategy(&mut self, strategy: WriteStrategy) {
546 self.strategy = strategy;
547 }
548
549 pub(super) fn buffer<BB: Buf + Into<B>>(&mut self, mut buf: BB) {
550 debug_assert!(buf.has_remaining());
551 match self.strategy {
552 WriteStrategy::Flatten => {
553 let head = self.headers_mut();
554
555 head.maybe_unshift(buf.remaining());
556 trace!(
557 self.len = head.remaining(),
558 buf.len = buf.remaining(),
559 "buffer.flatten"
560 );
561 loop {
564 let adv = {
565 let slice = buf.chunk();
566 if slice.is_empty() {
567 return;
568 }
569 head.bytes.extend_from_slice(slice);
570 slice.len()
571 };
572 buf.advance(adv);
573 }
574 }
575 WriteStrategy::Queue => {
576 trace!(
577 self.len = self.remaining(),
578 buf.len = buf.remaining(),
579 "buffer.queue"
580 );
581 self.queue.push(buf.into());
582 }
583 }
584 }
585
586 fn can_buffer(&self) -> bool {
587 match self.strategy {
588 WriteStrategy::Flatten => self.remaining() < self.max_buf_size,
589 WriteStrategy::Queue => {
590 self.queue.bufs_cnt() < MAX_BUF_LIST_BUFFERS && self.remaining() < self.max_buf_size
591 }
592 }
593 }
594
595 fn headers_mut(&mut self) -> &mut Cursor<Vec<u8>> {
596 debug_assert!(!self.queue.has_remaining());
597 &mut self.headers
598 }
599}
600
601impl<B: Buf> fmt::Debug for WriteBuf<B> {
602 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603 f.debug_struct("WriteBuf")
604 .field("remaining", &self.remaining())
605 .field("strategy", &self.strategy)
606 .finish_non_exhaustive()
607 }
608}
609
610impl<B: Buf> Buf for WriteBuf<B> {
611 #[inline]
612 fn remaining(&self) -> usize {
613 self.headers.remaining() + self.queue.remaining()
614 }
615
616 #[inline]
617 fn chunk(&self) -> &[u8] {
618 let headers = self.headers.chunk();
619 if !headers.is_empty() {
620 headers
621 } else {
622 self.queue.chunk()
623 }
624 }
625
626 #[inline]
627 fn advance(&mut self, cnt: usize) {
628 let hrem = self.headers.remaining();
629
630 match hrem.cmp(&cnt) {
631 cmp::Ordering::Equal => self.headers.reset(),
632 cmp::Ordering::Greater => self.headers.advance(cnt),
633 cmp::Ordering::Less => {
634 let qcnt = cnt - hrem;
635 self.headers.reset();
636 self.queue.advance(qcnt);
637 }
638 }
639 }
640
641 #[inline]
642 fn chunks_vectored<'data>(&'data self, dst: &mut [IoSlice<'data>]) -> usize {
643 let n = self.headers.chunks_vectored(dst);
644 self.queue.chunks_vectored(&mut dst[n..]) + n
645 }
646}
647
648#[derive(Debug)]
649enum WriteStrategy {
650 Flatten,
651 Queue,
652}
653
654#[cfg(test)]
655mod tests {
656 use super::*;
657 use crate::common::io::Compat;
658 use std::time::Duration;
659
660 use tokio_test::io::Builder as Mock;
661
662 #[tokio::test]
663 #[ignore]
664 async fn iobuf_write_empty_slice() {
665 }
682
683 #[cfg(not(miri))]
684 #[tokio::test]
685 async fn parse_reads_until_blocked() {
686 use crate::proto::h1::ClientTransaction;
687
688 let _ = pretty_env_logger::try_init();
689 let mock = Mock::new()
690 .read(b"HTTP/1.1 200 OK\r\n")
692 .read(b"Server: hyper\r\n")
693 .wait(Duration::from_secs(1))
695 .build();
696
697 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
698
699 futures_util::future::poll_fn(|cx| {
702 let parse_ctx = ParseContext {
703 cached_headers: &mut None,
704 req_method: &mut None,
705 h1_parser_config: Default::default(),
706 h1_max_headers: None,
707 h1_max_header_size: None,
708 preserve_header_case: false,
709 #[cfg(feature = "ffi")]
710 preserve_header_order: false,
711 h09_responses: false,
712 #[cfg(feature = "client")]
713 on_informational: &mut None,
714 };
715 assert!(buffered
716 .parse::<ClientTransaction>(cx, parse_ctx)
717 .is_pending());
718 Poll::Ready(())
719 })
720 .await;
721
722 assert_eq!(
723 buffered.read_buf,
724 b"HTTP/1.1 200 OK\r\nServer: hyper\r\n"[..]
725 );
726 }
727
728 #[test]
729 fn read_strategy_adaptive_increments() {
730 let mut strategy = ReadStrategy::default();
731 assert_eq!(strategy.next(), 8192);
732
733 strategy.record(8192);
735 assert_eq!(strategy.next(), 16384);
736
737 strategy.record(16384);
738 assert_eq!(strategy.next(), 32768);
739
740 strategy.record(usize::MAX);
742 assert_eq!(strategy.next(), 65536);
743
744 let max = strategy.max();
745 while strategy.next() < max {
746 strategy.record(max);
747 }
748
749 assert_eq!(strategy.next(), max, "never goes over max");
750 strategy.record(max + 1);
751 assert_eq!(strategy.next(), max, "never goes over max");
752 }
753
754 #[test]
755 fn read_strategy_adaptive_decrements() {
756 let mut strategy = ReadStrategy::default();
757 strategy.record(8192);
758 assert_eq!(strategy.next(), 16384);
759
760 strategy.record(1);
761 assert_eq!(
762 strategy.next(),
763 16384,
764 "first smaller record doesn't decrement yet"
765 );
766 strategy.record(8192);
767 assert_eq!(strategy.next(), 16384, "record was with range");
768
769 strategy.record(1);
770 assert_eq!(
771 strategy.next(),
772 16384,
773 "in-range record should make this the 'first' again"
774 );
775
776 strategy.record(1);
777 assert_eq!(strategy.next(), 8192, "second smaller record decrements");
778
779 strategy.record(1);
780 assert_eq!(strategy.next(), 8192, "first doesn't decrement");
781 strategy.record(1);
782 assert_eq!(strategy.next(), 8192, "doesn't decrement under minimum");
783 }
784
785 #[test]
786 fn read_strategy_adaptive_stays_the_same() {
787 let mut strategy = ReadStrategy::default();
788 strategy.record(8192);
789 assert_eq!(strategy.next(), 16384);
790
791 strategy.record(8193);
792 assert_eq!(
793 strategy.next(),
794 16384,
795 "first smaller record doesn't decrement yet"
796 );
797
798 strategy.record(8193);
799 assert_eq!(
800 strategy.next(),
801 16384,
802 "with current step does not decrement"
803 );
804 }
805
806 #[test]
807 fn read_strategy_adaptive_max_fuzz() {
808 fn fuzz(max: usize) {
809 let mut strategy = ReadStrategy::with_max(max);
810 while strategy.next() < max {
811 strategy.record(usize::MAX);
812 }
813 let mut next = strategy.next();
814 while next > 8192 {
815 strategy.record(1);
816 strategy.record(1);
817 next = strategy.next();
818 assert!(
819 next.is_power_of_two(),
820 "decrement should be powers of two: {} (max = {})",
821 next,
822 max,
823 );
824 }
825 }
826
827 let mut max = 8192;
828 while max < usize::MAX {
829 fuzz(max);
830 max = (max / 2).saturating_mul(3);
831 }
832 fuzz(usize::MAX);
833 }
834
835 #[test]
836 #[should_panic]
837 #[cfg(debug_assertions)] fn write_buf_requires_non_empty_bufs() {
839 let mock = Mock::new().build();
840 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
841
842 buffered.buffer(Cursor::new(Vec::new()));
843 }
844
845 #[cfg(not(miri))]
869 #[tokio::test]
870 async fn write_buf_flatten() {
871 let _ = pretty_env_logger::try_init();
872
873 let mock = Mock::new().write(b"hello world, it's hyper!").build();
874
875 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
876 buffered.write_buf.set_strategy(WriteStrategy::Flatten);
877
878 buffered.headers_buf().extend(b"hello ");
879 buffered.buffer(Cursor::new(b"world, ".to_vec()));
880 buffered.buffer(Cursor::new(b"it's ".to_vec()));
881 buffered.buffer(Cursor::new(b"hyper!".to_vec()));
882 assert_eq!(buffered.write_buf.queue.bufs_cnt(), 0);
883
884 buffered.flush().await.expect("flush");
885 }
886
887 #[test]
888 fn write_buf_flatten_partially_flushed() {
889 let _ = pretty_env_logger::try_init();
890
891 let b = |s: &str| Cursor::new(s.as_bytes().to_vec());
892
893 let mut write_buf = WriteBuf::<Cursor<Vec<u8>>>::new(WriteStrategy::Flatten);
894
895 write_buf.buffer(b("hello "));
896 write_buf.buffer(b("world, "));
897
898 assert_eq!(write_buf.chunk(), b"hello world, ");
899
900 write_buf.advance(11);
902
903 assert_eq!(write_buf.chunk(), b", ");
904 assert_eq!(write_buf.headers.pos, 11);
905 assert_eq!(write_buf.headers.bytes.capacity(), INIT_BUFFER_SIZE);
906
907 write_buf.buffer(b("it's hyper!"));
909
910 assert_eq!(write_buf.chunk(), b", it's hyper!");
911 assert_eq!(write_buf.headers.pos, 11);
912
913 let rem1 = write_buf.remaining();
914 let cap = write_buf.headers.bytes.capacity();
915
916 write_buf.buffer(Cursor::new(vec![b'X'; cap]));
918 assert_eq!(write_buf.remaining(), cap + rem1);
919 assert_eq!(write_buf.headers.pos, 0);
920 }
921
922 #[cfg(not(miri))]
923 #[tokio::test]
924 async fn write_buf_queue_disable_auto() {
925 let _ = pretty_env_logger::try_init();
926
927 let mock = Mock::new()
928 .write(b"hello ")
929 .write(b"world, ")
930 .write(b"it's ")
931 .write(b"hyper!")
932 .build();
933
934 let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(Compat::new(mock));
935 buffered.write_buf.set_strategy(WriteStrategy::Queue);
936
937 buffered.headers_buf().extend(b"hello ");
941 buffered.buffer(Cursor::new(b"world, ".to_vec()));
942 buffered.buffer(Cursor::new(b"it's ".to_vec()));
943 buffered.buffer(Cursor::new(b"hyper!".to_vec()));
944 assert_eq!(buffered.write_buf.queue.bufs_cnt(), 3);
945
946 buffered.flush().await.expect("flush");
947
948 assert_eq!(buffered.write_buf.queue.bufs_cnt(), 0);
949 }
950
951 }