body_image/
lib.rs

1//! Provides a uniform access strategy to HTTP bodies in RAM, or buffered to a
2//! temporary file, and optionally memory mapped.
3//!
4//! [`BodyImage`], [`BodySink`] and supporting types provide a strategy for
5//! safely handling potentially large HTTP request or response bodies without
6//! risk of allocation failure, or the need to impose awkwardly low size limits
7//! in the face of high concurrency. [`Tunables`] size thresholds can be used
8//! to decide when to accumulate the body in RAM vs. the filesystem, including
9//! when the length is unknown in advance.
10//!
11//! A [`Dialog`] defines a complete HTTP request and response recording, using
12//! `BodyImage` for the request and response bodies and _http_ crate types for
13//! the headers and other components.
14//!
15//! See the top-level (project workspace) [README][ws-readme] for additional
16//! rationale.
17//!
18//! ## Optional Features
19//!
20//! The following features may be enabled or disabled at build time. All are
21//! enabled by default.
22//!
23//! _mmap:_ Adds `BodyImage::mem_map` support for memory mapping from `FsRead`
24//! state.
25//!
26//! ## Related Crates
27//!
28//! _[barc]:_ **B**ody **Arc**hive container file reader and writer, for
29//! serializing `Dialog` records. See also _[barc-cli]_.
30//!
31//! _[body-image-futio]:_ Asynchronous HTTP integration with _futures_, _http_,
32//! _hyper_, and _tokio_ for `BodyImage`/`BodySink`, for both client and server
33//! use.
34//!
35//! [ws-readme]: https://github.com/dekellum/body-image
36//! [barc]: https://docs.rs/crate/barc
37//! [barc-cli]: https://docs.rs/crate/barc-cli
38//! [body-image-futio]: https://docs.rs/crate/body-image-futio
39
40#![warn(rust_2018_idioms)]
41
42use std::error::Error as StdError;
43use std::env;
44use std::fmt;
45use std::fs::File;
46use std::io;
47use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
48use std::mem;
49use std::path::Path;
50use std::sync::Arc;
51
52use bytes::{Bytes, BytesMut, BufMut};
53
54use tao_log::{debug, warn};
55use olio::io::GatheringReader;
56use olio::fs::rc::{ReadPos, ReadSlice};
57use tempfile::tempfile_in;
58
59#[cfg(feature = "mmap")] #[doc(hidden)] pub mod _mem_handle_ext;
60
61#[cfg(feature = "mmap")]
62use {
63    memmap::Mmap,
64    olio::mem::{MemAdvice, MemHandle},
65    _mem_handle_ext::MemHandleExt,
66};
67
68/// The crate version string.
69pub static VERSION: &str               = env!("CARGO_PKG_VERSION");
70
71/// Error enumeration for `BodyImage` and `BodySink` types.
72///
73/// This may be extended in the future so exhaustive matching is gently
74/// discouraged with an unused variant.
75#[derive(Debug)]
76pub enum BodyError {
77    /// Error for when `Tunables::max_body` length is exceeded.
78    BodyTooLong(u64),
79
80    /// IO error associated with file creation/writes `FsWrite`, reads
81    /// `FsRead`, or memory mapping.
82    Io(io::Error),
83
84    /// Unused variant to both enable non-exhaustive matching and warn against
85    /// exhaustive matching.
86    _FutureProof
87}
88
89impl fmt::Display for BodyError {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match *self {
92            BodyError::BodyTooLong(l) =>
93                write!(
94                    f, "Body length of {}+ bytes exceeds Tunables::max_body",
95                    l
96                ),
97            BodyError::Io(ref e) =>
98                write!(f, "Body I/O: {}", e),
99            BodyError::_FutureProof =>
100                unreachable!("Don't abuse the _FutureProof!")
101        }
102    }
103}
104
105impl StdError for BodyError {
106    fn source(&self) -> Option<&(dyn StdError + 'static)> {
107        match *self {
108            BodyError::Io(ref e) => Some(e),
109            _ => None
110        }
111    }
112}
113
114impl From<io::Error> for BodyError {
115    fn from(err: io::Error) -> BodyError {
116        BodyError::Io(err)
117    }
118}
119
120/// A logical buffer of bytes, which may or may not be RAM resident.
121///
122/// Besides a few immediate/convenience constructors found here, use
123/// [`BodySink`] for the incremental or stream-oriented
124/// collection of bytes to produce a `BodyImage`.
125///
126/// A `BodyImage` is always in one of the following states, as a buffering
127/// strategy:
128///
129/// `Ram`
130/// : A vector of zero, one, or many discontinuous (AKA scattered) byte
131///   buffers in Random Access Memory. This state is also used to represent
132///   an empty body (without allocation).
133///
134/// `FsRead`
135/// : Body in a (temporary) file, ready for position based, sequential read.
136///
137/// `MemMap`
138/// : Body in a memory mapped file, ready for random access read (default
139///   *mmap* feature)
140///
141/// All states support concurrent reads. `BodyImage` is `Send`, `Sync`, and
142/// supports low-cost shallow `Clone` via internal (atomic) reference
143/// counting.
144#[derive(Clone, Debug)]
145pub struct BodyImage {
146    state: ImageState,
147    len: u64
148}
149
150// Internal state enum for BodyImage
151#[derive(Clone)]
152enum ImageState {
153    Ram(Vec<Bytes>),
154    FsRead(Arc<File>),
155    FsReadSlice(ReadSlice),
156    #[cfg(feature = "mmap")]
157    MemMap(MemHandle<Mmap>),
158}
159
160/// A logical buffer of bytes, which may or may not be RAM resident, in the
161/// process of being written.
162///
163/// This is the write-side corollary to [`BodyImage`].
164/// A `BodySink` is always in one of the following states, as a buffering
165/// strategy:
166///
167/// `Ram`
168/// : A vector of zero, one, or many discontinuous (AKA scattered) byte
169///   buffers in Random Access Memory. This state is also used to represent
170///   an empty body (without allocation).
171///
172/// `FsWrite`
173/// : Body being written to a (temporary) file.
174#[derive(Debug)]
175pub struct BodySink {
176    state: SinkState,
177    len: u64
178}
179
180enum SinkState {
181    Ram(Vec<Bytes>),
182    FsWrite(File),
183}
184
185impl SinkState {
186    // Swap self with empty `Ram`, and return moved original.
187    // Warning: Be careful about exposing an invalid length when using.
188    fn cut(&mut self) -> Self {
189        mem::replace(self, SinkState::Ram(Vec::with_capacity(0)))
190    }
191}
192
193impl BodySink {
194    /// Create new empty instance, which does not pre-allocate. The state is
195    /// `Ram` with a zero-capacity vector.
196    pub fn empty() -> BodySink {
197        BodySink::with_ram_buffers(0)
198    }
199
200    /// Create a new `Ram` instance by pre-allocating a vector of buffers
201    /// based on the given size estimate in bytes, assuming 8 KiB
202    /// buffers. With a size_estimate of 0, this is the same as `empty`.
203    pub fn with_ram(size_estimate: u64) -> BodySink {
204        if size_estimate == 0 {
205            BodySink::empty()
206        } else {
207            // Estimate buffers based an 8 KiB buffer size + 1.
208            let cap = (size_estimate / 0x2000 + 1) as usize;
209            BodySink::with_ram_buffers(cap)
210        }
211    }
212
213    /// Create a new `Ram` instance by pre-allocating a vector of the
214    /// specified capacity.
215    pub fn with_ram_buffers(capacity: usize) -> BodySink {
216        BodySink {
217            state: SinkState::Ram(Vec::with_capacity(capacity)),
218            len: 0
219        }
220    }
221
222    /// Create a new instance in state `FsWrite`, using a new temporary file
223    /// created in dir.
224    pub fn with_fs<P>(dir: P) -> Result<BodySink, BodyError>
225        where P: AsRef<Path>
226    {
227        let f = tempfile_in(dir)?;
228        Ok(BodySink {
229            state: SinkState::FsWrite(f),
230            len: 0
231        })
232    }
233
234    /// Return true if in state `Ram`.
235    pub fn is_ram(&self) -> bool {
236        match self.state {
237            SinkState::Ram(_) => true,
238            _ => false
239        }
240    }
241
242    /// Return true if body is empty.
243    pub fn is_empty(&self) -> bool {
244        self.len == 0
245    }
246
247    /// Return the current length of body in bytes.
248    pub fn len(&self) -> u64 {
249        self.len
250    }
251
252    /// Push `Bytes`-convertable buffer to end of `Ram` vector, or by writing
253    /// to end of `FsWrite` file.
254    #[deprecated(since="2.0.0", note="use `push` or `write_all` instead")]
255    #[inline]
256    pub fn save<T>(&mut self, buf: T) -> Result<(), BodyError>
257        where T: Into<Bytes>
258    {
259        let buf = buf.into();
260        self.push(buf).map_err(BodyError::from)
261    }
262
263    /// Push `Bytes`-convertable buffer to end of `Ram` vector, or by writing
264    /// to end of `FsWrite` file.
265    ///
266    /// Note the additional `Into<Bytes>` bound vs `write_all`. When in state
267    /// `Ram`, `push` is more efficient than `write_all` _if_ `Into<Bytes>`
268    /// does not copy. When in state `FsWrite` it is the same.
269    pub fn push<T>(&mut self, buf: T) -> Result<(), io::Error>
270        where T: Into<Bytes> + AsRef<[u8]>
271    {
272        match self.state {
273            SinkState::Ram(ref mut v) => {
274                let buf = buf.into();
275                let len = buf.len() as u64;
276                if len > 0 {
277                    v.push(buf);
278                    self.len += len;
279                }
280            }
281            SinkState::FsWrite(ref mut f) => {
282                let buf = buf.as_ref();
283                let len = buf.len() as u64;
284                if len > 0 {
285                    f.write_all(buf)?;
286                    self.len += len;
287                }
288            }
289        }
290        Ok(())
291    }
292
293    /// Write all bytes to end of self.
294    ///
295    /// Prefer `push` to this method if the additional bound is met. Only when
296    /// in state `FsWrite` is this method copy free.
297    pub fn write_all<T>(&mut self, buf: T) -> Result<(), io::Error>
298        where T: AsRef<[u8]>
299    {
300        let buf = buf.as_ref();
301        let len = buf.len() as u64;
302        if len > 0 {
303            match self.state {
304                SinkState::Ram(ref mut v) => {
305                    v.push(Bytes::copy_from_slice(buf));
306                }
307                SinkState::FsWrite(ref mut f) => {
308                    f.write_all(buf)?;
309                }
310            }
311            self.len += len;
312        }
313        Ok(())
314    }
315
316    /// If `Ram`, convert to `FsWrite` by writing all bytes in RAM to a
317    /// temporary file, created in dir.  No-op if already `FsWrite`. Buffers
318    /// are eagerly dropped as they are written. As a consequence, if any
319    /// error result is returned (e.g. opening or writing to the file), self
320    /// will be empty and in the `Ram` state. There is no practical recovery
321    /// for the original body.
322    pub fn write_back<P>(&mut self, dir: P) -> Result<&mut Self, BodyError>
323        where P: AsRef<Path>
324    {
325        if self.is_ram() {
326            if let SinkState::Ram(v) = self.state.cut() {
327                let olen = self.len;
328                self.len = 0;
329                let mut f = tempfile_in(dir)?;
330                for b in v {
331                    f.write_all(&b)?;
332                    drop::<Bytes>(b); // Ensure ASAP drop
333                }
334                self.state = SinkState::FsWrite(f);
335                self.len = olen;
336            }
337        }
338        Ok(self)
339    }
340
341    /// Consumes self, converts and returns as `BodyImage` ready for read.
342    pub fn prepare(self) -> Result<BodyImage, BodyError> {
343        match self.state {
344            SinkState::Ram(v) => {
345                Ok(BodyImage {
346                    state: ImageState::Ram(v),
347                    len: self.len
348                })
349            }
350            SinkState::FsWrite(mut f) => {
351                // Protect against empty files, which would fail if
352                // mem_map'd, by replacing with empty `Ram` state
353                if self.len == 0 {
354                    Ok(BodyImage::empty())
355                } else {
356                    f.flush()?;
357                    f.seek(SeekFrom::Start(0))?;
358                    Ok(BodyImage {
359                        state: ImageState::FsRead(Arc::new(f)),
360                        len: self.len
361                    })
362                }
363            }
364        }
365    }
366}
367
368impl Default for BodySink {
369    fn default() -> BodySink { BodySink::empty() }
370}
371
372impl fmt::Debug for SinkState {
373    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374        match *self {
375            SinkState::Ram(ref v) => {
376                // Avoids showing all buffers as u8 lists
377                f.debug_struct("Ram(Vec<Bytes>)")
378                    .field("capacity", &v.capacity())
379                    .field("len", &v.len())
380                    .finish()
381            }
382            SinkState::FsWrite(ref file) => {
383                f.debug_tuple("FsWrite")
384                    .field(file)
385                    .finish()
386            }
387        }
388    }
389}
390
391impl ImageState {
392    fn empty() -> ImageState {
393        ImageState::Ram(Vec::with_capacity(0))
394    }
395
396    // Swap self with empty `Ram`, and return moved original.
397    // Warning: Be careful about exposing an invalid length when using.
398    fn cut(&mut self) -> Self {
399        mem::replace(self, ImageState::empty())
400    }
401}
402
403impl BodyImage {
404    /// Create new empty instance with no allocation. The state is
405    /// `Ram` with a zero-capacity vector.
406    pub fn empty() -> BodyImage {
407        BodyImage {
408            state: ImageState::empty(),
409            len: 0
410        }
411    }
412
413    /// Create a new `FsRead` instance based on an existing `File`. The fixed
414    /// length is used to report `BodyImage::len` and may be obtained using
415    /// `File::metadata`. If the provided length is zero, this returns as per
416    /// `BodyImage::empty()` instead. Attempts to read from the returned
417    /// `BodyImage` can fail if the file is not open for read.
418    ///
419    /// ### Safety
420    ///
421    /// Use of this constructor is potentially unsafe when the *mmap* feature
422    /// enabled and once `mem_map` is called:
423    ///
424    /// * The `mem_map` call will fail if the file is zero length or not open
425    /// for read.
426    ///
427    /// * Any concurrent writes to the file, or file system modifications
428    /// while under use in `MemMap` state may lead to *Undefined Behavior*
429    /// (UB).
430    #[cfg(feature = "mmap")]
431    pub unsafe fn from_file(file: File, length: u64) -> BodyImage {
432        image_from_file(file, length)
433    }
434
435    /// Create a new `FsRead` instance based on an existing `File`. The fixed
436    /// length is used to report `BodyImage::len` and may be obtained using
437    /// `File::metadata`. If the provided length is zero, this returns as per
438    /// `BodyImage::empty()` instead. Attempts to read from or `mem_map` the
439    /// returned `BodyImage` can fail if the file is not open for read or is
440    /// zero length.
441    #[cfg(not(feature = "mmap"))]
442    pub fn from_file(file: File, length: u64) -> BodyImage {
443        image_from_file(file, length)
444    }
445
446    /// Create new instance from a single byte slice.
447    pub fn from_slice<T>(bytes: T) -> BodyImage
448        where T: Into<Bytes>
449    {
450        let mut bs = BodySink::with_ram_buffers(1);
451        let buf = bytes.into();
452        bs.push(buf).expect("push is safe to Ram");
453        bs.prepare().expect("prepare is safe to Ram")
454    }
455
456    /// Create a new instance based on a `ReadSlice`. The `BodyImage::len`
457    /// will be as per `ReadSlice::len`, and if zero, this returns as per
458    /// `BodyImage::empty()`. Attempts to read from the returned
459    /// `BodyImage` can fail if the file is not open for read.
460    ///
461    /// ### Safety
462    ///
463    /// Use of this constructor is potentially unsafe when the *mmap* feature
464    /// enabled and once `mem_map` is called:
465    ///
466    /// * The `mem_map` call will fail if the file is zero length or not open
467    /// for read.
468    ///
469    /// * Any concurrent writes to the file, or file system modifications
470    /// while under use in `MemMap` state may lead to *Undefined Behavior*
471    /// (UB).
472    #[cfg(feature = "mmap")]
473    pub unsafe fn from_read_slice(rslice: ReadSlice) -> BodyImage {
474        image_from_read_slice(rslice)
475    }
476
477    /// Create a new instance based on a `ReadSlice`. The `BodyImage::len`
478    /// will be as per `ReadSlice::len`, and if zero, this returns as per
479    /// `BodyImage::empty()`. Attempts to read from or `mem_map` the returned
480    /// `BodyImage` can fail if the file is not open for read or is zero
481    /// length.
482    #[cfg(not(feature = "mmap"))]
483    pub fn from_read_slice(rslice: ReadSlice) -> BodyImage {
484        image_from_read_slice(rslice)
485    }
486
487    /// Return true if in state `Ram`.
488    pub fn is_ram(&self) -> bool {
489        match self.state {
490            ImageState::Ram(_) => true,
491            _ => false
492        }
493    }
494
495    /// Return true if in state `MemMap`.
496    #[cfg(feature = "mmap")]
497    pub fn is_mem_map(&self) -> bool {
498        match self.state {
499            ImageState::MemMap(_) => true,
500            _ => false
501        }
502    }
503
504    /// Return the current length of body in bytes.
505    pub fn len(&self) -> u64 {
506        self.len
507    }
508
509    /// Return true if body is empty.
510    pub fn is_empty(&self) -> bool {
511        self.len == 0
512    }
513
514    /// If `FsRead`, convert to `MemMap` by memory mapping the file.
515    ///
516    /// Under normal construction via `BodySink` in `FsWrite` state, this
517    /// method is safe, because no other thread or process has access to the
518    /// underlying file. Note the potential safety requirements via
519    /// [`from_file`](BodyImage::from_file) however.
520    #[cfg(feature = "mmap")]
521    pub fn mem_map(&mut self) -> Result<&mut Self, BodyError> {
522        let map = match self.state {
523            ImageState::FsRead(ref file) => {
524                assert!(self.len > 0);
525                unsafe { Mmap::map(&file) }?
526            }
527            ImageState::FsReadSlice(ref rslice) => {
528                rslice.mem_map()?
529            }
530            _ => return Ok(self)
531        };
532
533        self.state = ImageState::MemMap(MemHandle::new(map));
534        Ok(self)
535    }
536
537    /// If `Ram` with 2 or more buffers, *gather* by copying into a single
538    /// contiguous buffer with the same total length. No-op for other
539    /// states. Buffers are eagerly dropped as they are copied. Possibly in
540    /// combination with `mem_map`, this can be used to ensure `Cursor` (and
541    /// `&[u8]` slice) access via `reader`, at the cost of the copy.
542    pub fn gather(&mut self) -> &mut Self {
543        let scattered = if let ImageState::Ram(ref v) = self.state {
544            v.len() > 1
545        } else {
546            false
547        };
548
549        if scattered {
550            if let ImageState::Ram(v) = self.state.cut() {
551                let mut newb = BytesMut::with_capacity(self.len as usize);
552                for b in v {
553                    newb.put_slice(&b);
554                    drop::<Bytes>(b); // Ensure ASAP drop
555                }
556                let newb = newb.freeze();
557                assert_eq!(newb.len() as u64, self.len);
558                self.state = ImageState::Ram(vec![newb]);
559            }
560        }
561        self
562    }
563
564    /// Return a new `BodyReader` enum over self. The enum provides a
565    /// consistent `Read` reference, or can be destructured for access to
566    /// the specific concrete types.
567    pub fn reader(&self) -> BodyReader<'_> {
568        match self.state {
569            ImageState::Ram(ref v) => {
570                if v.is_empty() {
571                    BodyReader::Contiguous(Cursor::new(&[]))
572                } else if v.len() == 1 {
573                    BodyReader::Contiguous(Cursor::new(&v[0]))
574                } else {
575                    BodyReader::Scattered(GatheringReader::new(v))
576                }
577            }
578            ImageState::FsRead(ref f) => {
579                BodyReader::FileSlice(ReadSlice::new(f.clone(), 0, self.len))
580            }
581            ImageState::FsReadSlice(ref rslice) => {
582                BodyReader::FileSlice(rslice.clone())
583            }
584            #[cfg(feature = "mmap")]
585            ImageState::MemMap(ref m) => {
586                BodyReader::Contiguous(Cursor::new(m))
587            }
588        }
589    }
590
591    /// Consume self, *exploding* into an [`ExplodedImage`] variant.
592    pub fn explode(self) -> ExplodedImage {
593        match self.state {
594            ImageState::Ram(v) => ExplodedImage::Ram(v),
595            ImageState::FsRead(f) => {
596                ExplodedImage::FsRead(ReadSlice::new(f, 0, self.len))
597            }
598            ImageState::FsReadSlice(rs) => ExplodedImage::FsRead(rs),
599            #[cfg(feature = "mmap")]
600            ImageState::MemMap(m) => ExplodedImage::MemMap(m),
601        }
602    }
603
604    /// Given a `Read` reference, a length estimate in bytes and `Tunables`,
605    /// read and prepare a new `BodyImage`. `Tunables`, the estimate and
606    /// actual length read will determine which buffering strategy is
607    /// used. The length estimate provides a hint to use the file system from
608    /// the start, which is more optimal than writing out accumulated `Ram`
609    /// buffers later. If the length can't be estimated, use zero (0).
610    ///
611    /// The `Read` is passed by reference for backward compatibility with its
612    /// original non-generic form as `&mut dyn Read`. [C-RW-VALUE] prefers
613    /// pass by value, but this would now be a breaking change.
614    ///
615    /// [C-RW-VALUE]: https://rust-lang.github.io/api-guidelines/interoperability.html#generic-readerwriter-functions-take-r-read-and-w-write-by-value-c-rw-value
616    pub fn read_from<R>(rin: &mut R, len_estimate: u64, tune: &Tunables)
617        -> Result<BodyImage, BodyError>
618        where R: Read + ?Sized
619    {
620        if len_estimate > tune.max_body_ram() {
621            let b = BodySink::with_fs(tune.temp_dir())?;
622            return read_to_body_fs(rin, b, tune);
623        }
624
625        let mut body = BodySink::with_ram(len_estimate);
626
627        let mut size: u64 = 0;
628        'eof: loop {
629            let mut buf = BytesMut::with_capacity(tune.buffer_size_ram());
630            'fill: loop {
631                let b = unsafe { &mut *(
632                    buf.chunk_mut() as *mut _
633                        as *mut [mem::MaybeUninit<u8>]
634                        as *mut [u8]
635                )};
636                let len = match rin.read(b) {
637                    Ok(len) => len,
638                    Err(e) => {
639                        if e.kind() == ErrorKind::Interrupted {
640                            continue;
641                        } else {
642                            return Err(e.into());
643                        }
644                    }
645                };
646                if len == 0 {
647                    break 'fill; // not 'eof as may have bytes in buf
648
649                }
650                unsafe { buf.advance_mut(len); }
651
652                if buf.remaining_mut() < 1024 {
653                    break 'fill;
654                }
655            }
656            let len = buf.len() as u64;
657            if len == 0 {
658                break 'eof;
659            }
660            size += len;
661            if size > tune.max_body() {
662                return Err(BodyError::BodyTooLong(size));
663            }
664            if size > tune.max_body_ram() {
665                body.write_back(tune.temp_dir())?;
666                debug!("Write (Fs) buffer len {}", len);
667                body.write_all(&buf)?;
668                return read_to_body_fs(rin, body, tune)
669            }
670            debug!("Saved (Ram) buffer len {}", len);
671            body.push(buf.freeze())?;
672        }
673        let body = body.prepare()?;
674        Ok(body)
675    }
676
677    /// Write self to `out` and return length. If `FsRead` this is performed
678    /// using `std::io::copy` with `ReadPos` as input.
679    ///
680    /// The `Write` is passed by reference for backward compatibility with its
681    /// original non-generic form as `&mut dyn Write`. [C-RW-VALUE] prefers
682    /// pass by value, but this would now be a breaking change.
683    /// [`std::io::copy`] is presumably in the same position.
684    ///
685    /// [C-RW-VALUE]: https://rust-lang.github.io/api-guidelines/interoperability.html#generic-readerwriter-functions-take-r-read-and-w-write-by-value-c-rw-value
686    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
687    pub fn write_to<W>(&self, out: &mut W) -> Result<u64, BodyError>
688        where W: Write + ?Sized
689    {
690        match self.state {
691            ImageState::Ram(ref v) => {
692                for b in v {
693                    out.write_all(b)?;
694                }
695            }
696            #[cfg(feature = "mmap")]
697            ImageState::MemMap(ref mh) => {
698                mh.tmp_advise(MemAdvice::Sequential, || out.write_all(mh))?;
699            }
700            ImageState::FsRead(ref f) => {
701                let mut rp = ReadPos::new(f.clone(), self.len);
702                io::copy(&mut rp, out)?;
703            }
704            ImageState::FsReadSlice(ref rslice) => {
705                let mut rs = rslice.clone();
706                io::copy(&mut rs, out)?;
707            }
708        }
709        Ok(self.len)
710    }
711}
712
713// Create a new `FsRead` instance based on an existing `File`.
714fn image_from_file(file: File, length: u64) -> BodyImage {
715    if length > 0 {
716        BodyImage {
717            state: ImageState::FsRead(Arc::new(file)),
718            len: length
719        }
720    } else {
721        BodyImage::empty()
722    }
723}
724
725// Create a new `FsRead` instance based on an existing `ReadSlice`.
726fn image_from_read_slice(rslice: ReadSlice) -> BodyImage {
727    let len = rslice.len();
728    if len > 0 {
729        BodyImage { state: ImageState::FsReadSlice(rslice), len }
730    } else {
731        BodyImage::empty()
732    }
733}
734
735// Read all bytes from r, consume and write to a `BodySink` in state
736// `FsWrite`, returning a final prepared `BodyImage`.
737fn read_to_body_fs<R>(r: &mut R, mut body: BodySink, tune: &Tunables)
738    -> Result<BodyImage, BodyError>
739    where R: Read + ?Sized
740{
741    assert!(!body.is_ram());
742
743    let mut size: u64 = 0;
744    let mut buf = BytesMut::with_capacity(tune.buffer_size_fs());
745    loop {
746        let b = unsafe { &mut *(
747            buf.chunk_mut() as *mut _
748                as *mut [mem::MaybeUninit<u8>]
749                as *mut [u8]
750        )};
751        let len = match r.read(b) {
752            Ok(l) => l,
753            Err(e) => {
754                if e.kind() == ErrorKind::Interrupted {
755                    continue;
756                } else {
757                    return Err(e.into());
758                }
759            }
760        };
761        if len == 0 {
762            break;
763        }
764        unsafe { buf.advance_mut(len); }
765
766        size += len as u64;
767        if size > tune.max_body() {
768            return Err(BodyError::BodyTooLong(size));
769        }
770        debug!("Write (Fs) buffer len {}", len);
771        body.write_all(&buf)?;
772        buf.clear();
773    }
774    let body = body.prepare()?;
775    Ok(body)
776}
777
778impl Default for BodyImage {
779    fn default() -> BodyImage { BodyImage::empty() }
780}
781
782impl fmt::Debug for ImageState {
783    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
784        match *self {
785            ImageState::Ram(ref v) => {
786                // Avoids showing all buffers as u8 lists
787                f.debug_struct("Ram(Vec<Bytes>)")
788                    .field("capacity", &v.capacity())
789                    .field("len", &v.len())
790                    .finish()
791            }
792            ImageState::FsRead(ref file) => {
793                f.debug_tuple("FsRead")
794                    .field(file)
795                    .finish()
796            }
797            ImageState::FsReadSlice(ref rslice) => {
798                f.debug_tuple("FsReadSlice")
799                    .field(rslice)
800                    .finish()
801            }
802            #[cfg(feature = "mmap")]
803            ImageState::MemMap(ref m) => {
804                f.debug_tuple("MemMap")
805                    .field(m)
806                    .finish()
807            }
808        }
809    }
810}
811
812/// *Exploded* representation of the possible `BodyImage` states, obtained via
813/// [`BodyImage::explode()`].
814pub enum ExplodedImage {
815    Ram(Vec<Bytes>),
816    FsRead(ReadSlice),
817    #[cfg(feature = "mmap")]
818    MemMap(MemHandle<Mmap>),
819}
820
821/// Provides a `Read` reference for a `BodyImage` in any state.
822pub enum BodyReader<'a> {
823    /// `Cursor` over a contiguous single RAM buffer, from `Ram` or
824    /// `MemMap`. Also used for the empty case. `Cursor::into_inner` may be
825    /// used for direct access to the memory byte slice.
826    Contiguous(Cursor<&'a [u8]>),
827
828    /// `GatheringReader` providing `Read` over 2 or more scattered RAM
829    /// buffers.
830    Scattered(GatheringReader<'a, Bytes>),
831
832    /// `ReadSlice` providing instance independent, unbuffered `Read` and
833    /// `Seek` for BodyImage `FsRead` state, limited to a range within an
834    /// underlying file. Consider wrapping this in `std::io::BufReader` if
835    /// performing many small reads.
836    FileSlice(ReadSlice),
837}
838
839impl<'a> Read for BodyReader<'a> {
840    #[inline]
841    fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
842        match *self {
843            BodyReader::Contiguous(ref mut cursor) => cursor.read(buf),
844            BodyReader::Scattered(ref mut gatherer) => gatherer.read(buf),
845            BodyReader::FileSlice(ref mut rslice) => rslice.read(buf),
846        }
847    }
848}
849
850/// Extract of an HTTP request.
851///
852/// Alternate spelling of _prologue_.
853#[derive(Clone, Debug)]
854pub struct Prolog {
855    pub method:       http::Method,
856    pub url:          http::Uri,
857    pub req_headers:  http::HeaderMap,
858    pub req_body:     BodyImage,
859}
860
861/// Extract of an HTTP response.
862#[derive(Clone, Debug)]
863pub struct Epilog {
864    pub version:      http::Version,
865    pub status:       http::StatusCode,
866    pub res_headers:  http::HeaderMap,
867    pub res_body:     BodyImage,
868    pub res_decoded:  Vec<Encoding>,
869}
870
871/// A set of HTTP Transfer- or Content-Encoding values.
872///
873/// The `Display`/`ToString` representation is as per the HTTP header value.
874#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
875pub enum Encoding {
876    /// `Chunked` (transfer encoding) is typically applied or removed by HTTP
877    /// client and server implementations—even minimal ones.
878    Chunked,
879    Deflate,
880    Gzip,
881    Brotli,
882    /// This obsolete LZW format is not generally used or supported
883    /// currently, but is included for error reporting.
884    Compress,
885    /// May be used to explicitly indicate that an encoding previously applied
886    /// has been decoded (removed).
887    Identity,
888}
889
890impl fmt::Display for Encoding {
891    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
892        f.write_str(match *self {
893            Encoding::Chunked => "chunked",
894            Encoding::Deflate => "deflate",
895            Encoding::Gzip    => "gzip",
896            Encoding::Brotli  => "br",
897            Encoding::Compress  => "compress",
898            Encoding::Identity  => "identity",
899        })
900    }
901}
902
903/// An HTTP request and response recording.
904///
905/// This composed type has private fields but offers getters, mutable getters
906/// and setter methods. Several important getter methods are found in trait
907/// implementations [`RequestRecorded`](#impl-RequestRecorded) and
908/// [`Recorded`](#impl-Recorded).
909///
910/// It may be constructed via the `Prolog` and `Epilog` public structs and the
911/// [`explode`](Dialog::explode()) method can be used to extract the same.
912#[derive(Clone, Debug)]
913pub struct Dialog {
914    pro: Prolog,
915    epi: Epilog,
916}
917
918/// Access by reference for HTTP request recording types.
919pub trait RequestRecorded {
920    /// Map of HTTP request headers.
921    fn req_headers(&self) -> &http::HeaderMap;
922
923    /// Request body (e.g for HTTP POST, etc.) which may or may not be RAM
924    /// resident.
925    fn req_body(&self)    -> &BodyImage;
926}
927
928/// Access by reference for HTTP request (via `RequestRecorded`) and response
929/// recording types.
930pub trait Recorded: RequestRecorded {
931    /// Map of HTTP response headers.
932    fn res_headers(&self) -> &http::HeaderMap;
933
934    /// Response body which may or may not be RAM resident.
935    fn res_body(&self)    -> &BodyImage;
936}
937
938impl Dialog {
939    /// Construct from a `Prolog` and `Epilog`.
940    pub fn new(pro: Prolog, epi: Epilog) -> Dialog {
941        Dialog { pro, epi }
942    }
943
944    /// The HTTP method (verb), e.g. `GET`, `POST`, etc.
945    pub fn method(&self)      -> &http::Method         { &self.pro.method }
946
947    /// The complete URL as used in the request.
948    pub fn url(&self)         -> &http::Uri            { &self.pro.url }
949
950    /// A mutable reference to the request body. This is primarly provided
951    /// to allow state mutating operations such as `BodyImage::mem_map`.
952    pub fn req_body_mut(&mut self) -> &mut BodyImage {
953        &mut self.pro.req_body
954    }
955
956    /// The response status code.
957    pub fn res_status(&self)  -> http::StatusCode      { self.epi.status }
958
959    /// The response HTTP version.
960    pub fn res_version(&self) -> http::Version         { self.epi.version }
961
962    /// A list of encodings that were removed (decoded) to provide this
963    /// representation of the response body (`res_body`). May be empty.
964    pub fn res_decoded(&self) -> &Vec<Encoding>        { &self.epi.res_decoded }
965
966    /// Set a new response decoded list.
967    pub fn set_res_decoded(&mut self, decoded: Vec<Encoding>) {
968        self.epi.res_decoded = decoded;
969    }
970
971    /// A mutable reference to the response body. This is primarly provided
972    /// to allow state mutating operations such as `BodyImage::mem_map`.
973    pub fn res_body_mut(&mut self) -> &mut BodyImage   { &mut self.epi.res_body }
974
975    /// Set a new response body and decoded vector.
976    pub fn set_res_body_decoded(&mut self, body: BodyImage, decoded: Vec<Encoding>) {
977        self.epi.res_body = body;
978        self.epi.res_decoded = decoded;
979    }
980
981    /// Consume self, *exploding* into a (`Prolog`, `Epilog`) tuple (each
982    /// with public fields).
983    pub fn explode(self) -> (Prolog, Epilog) {
984        (self.pro, self.epi)
985    }
986}
987
988impl RequestRecorded for Dialog {
989    fn req_headers(&self) -> &http::HeaderMap      { &self.pro.req_headers }
990    fn req_body(&self)    -> &BodyImage            { &self.pro.req_body }
991}
992
993impl Recorded for Dialog {
994    fn res_headers(&self) -> &http::HeaderMap      { &self.epi.res_headers }
995    fn res_body(&self)    -> &BodyImage            { &self.epi.res_body }
996}
997
998/// A collection of size limits and performance tuning constants. Setters are
999/// available via [`Tuner`].
1000#[derive(Debug, Clone)]
1001pub struct Tunables {
1002    max_body_ram:            u64,
1003    max_body:                u64,
1004    buffer_size_ram:         usize,
1005    buffer_size_fs:          usize,
1006    size_estimate_deflate:   u16,
1007    size_estimate_gzip:      u16,
1008    size_estimate_brotli:    u16,
1009    temp_dir:                Arc<Path>,
1010}
1011
1012impl Tunables {
1013    /// Construct with default values.
1014    pub fn new() -> Tunables {
1015        Tunables {
1016            max_body_ram:       192 * 1024,
1017            max_body:   1024 * 1024 * 1024,
1018            buffer_size_ram:      8 * 1024,
1019            buffer_size_fs:      64 * 1024,
1020            size_estimate_deflate:       4,
1021            size_estimate_gzip:          5,
1022            size_estimate_brotli:        6,
1023            temp_dir: env::temp_dir().into(),
1024        }
1025    }
1026
1027    /// Return the maximum body size in bytes allowed in RAM, e.g. before
1028    /// writing to a temporary file, or memory mapping instead of direct, bulk
1029    /// read. Default: 192 KiB.
1030    pub fn max_body_ram(&self) -> u64 {
1031        self.max_body_ram
1032    }
1033
1034    /// Return the maximum body size in bytes allowed in any form (RAM or
1035    /// file). Default: 1 GiB.
1036    pub fn max_body(&self) -> u64 {
1037        self.max_body
1038    }
1039
1040    /// Return the buffer size in bytes to use when buffering to RAM.
1041    /// Default: 8 KiB.
1042    pub fn buffer_size_ram(&self) -> usize {
1043        self.buffer_size_ram
1044    }
1045
1046    /// Return the buffer size in bytes to use when buffering to/from
1047    /// the file-system. Default: 64 KiB.
1048    pub fn buffer_size_fs(&self) -> usize {
1049        self.buffer_size_fs
1050    }
1051
1052    /// Return the size estimate, as an integer multiple of the encoded buffer
1053    /// size, for the _deflate_ compression algorithm.  Default: 4.
1054    pub fn size_estimate_deflate(&self) -> u16 {
1055        self.size_estimate_deflate
1056    }
1057
1058    /// Return the size estimate, as an integer multiple of the encoded buffer
1059    /// size, for the _gzip_ compression algorithm.  Default: 5.
1060    pub fn size_estimate_gzip(&self) -> u16 {
1061        self.size_estimate_gzip
1062    }
1063
1064    /// Return the size estimate, as an integer multiple of the encoded buffer
1065    /// size, for the _Brotli_ compression algorithm.  Default: 6.
1066    pub fn size_estimate_brotli(&self) -> u16 {
1067        self.size_estimate_brotli
1068    }
1069
1070    /// Return the directory path in which to write temporary (`BodyImage`)
1071    /// files.  Default: `std::env::temp_dir()`
1072    pub fn temp_dir(&self) -> &Path {
1073        &self.temp_dir
1074    }
1075
1076    /// Return a reference to the directory path in which to write temporary
1077    /// (`BodyImage`) files.  Default: `std::env::temp_dir()`
1078    pub fn temp_dir_rc(&self) -> Arc<Path> {
1079        self.temp_dir.clone()
1080    }
1081
1082}
1083
1084impl Default for Tunables {
1085    fn default() -> Self { Tunables::new() }
1086}
1087
1088/// A builder for `Tunables`. Invariants are asserted in the various setters
1089/// and `finish`.
1090#[derive(Clone)]
1091pub struct Tuner {
1092    template: Tunables
1093}
1094
1095impl Tuner {
1096    /// New `Tuner` with all `Tunables` defaults.
1097    pub fn new() -> Tuner {
1098        Tuner { template: Tunables::new() }
1099    }
1100
1101    /// Set the maximum body size in bytes allowed in RAM.
1102    pub fn set_max_body_ram(&mut self, size: u64) -> &mut Tuner {
1103        self.template.max_body_ram = size;
1104        self
1105    }
1106
1107    /// Set the maximum body size in bytes allowed in any form (RAM or
1108    /// file). This must be at least as large as `max_body_ram`, as asserted
1109    /// on `finish`.
1110    pub fn set_max_body(&mut self, size: u64) -> &mut Tuner {
1111        self.template.max_body = size;
1112        self
1113    }
1114
1115    /// Set the buffer size in bytes to use when buffering to RAM.
1116    pub fn set_buffer_size_ram(&mut self, size: usize) -> &mut Tuner {
1117        assert!(size > 0, "buffer_size_ram must be greater than zero");
1118        self.template.buffer_size_ram = size;
1119        self
1120    }
1121
1122    /// Set the buffer size in bytes to use when buffering to/from
1123    /// the file-system.
1124    pub fn set_buffer_size_fs(&mut self, size: usize) -> &mut Tuner {
1125        assert!(size > 0, "buffer_size_fs must be greater than zero");
1126        self.template.buffer_size_fs = size;
1127        self
1128    }
1129
1130    /// Set the size estimate, as an integer multiple of the encoded buffer
1131    /// size, for the _deflate_ compression algorithm.
1132    pub fn set_size_estimate_deflate(&mut self, multiple: u16) -> &mut Tuner {
1133        assert!(multiple > 0, "size_estimate_deflate must be >= 1");
1134        self.template.size_estimate_deflate = multiple;
1135        self
1136    }
1137
1138    /// Set the size estimate, as an integer multiple of the encoded buffer
1139    /// size, for the _gzip_ compression algorithm.
1140    pub fn set_size_estimate_gzip(&mut self, multiple: u16) -> &mut Tuner {
1141        assert!(multiple > 0, "size_estimate_gzip must be >= 1");
1142        self.template.size_estimate_gzip = multiple;
1143        self
1144    }
1145
1146    /// Set the size estimate, as an integer multiple of the encoded buffer
1147    /// size, for the _Brotli_ compression algorithm.
1148    pub fn set_size_estimate_brotli(&mut self, multiple: u16) -> &mut Tuner {
1149        assert!(multiple > 0, "size_estimate_brotli must be >= 1");
1150        self.template.size_estimate_brotli = multiple;
1151        self
1152    }
1153
1154    /// Set the path in which to write temporary files.
1155    pub fn set_temp_dir<P>(&mut self, path: P) -> &mut Tuner
1156        where P: AsRef<Path>
1157    {
1158        self.template.temp_dir = path.as_ref().into();
1159        self
1160    }
1161
1162    /// Finish building, asserting any remaining invariants, and return a new
1163    /// `Tunables` instance.
1164    pub fn finish(&self) -> Tunables {
1165        let t = self.template.clone();
1166        assert!(t.max_body_ram <= t.max_body,
1167                "max_body_ram can't be greater than max_body");
1168        t
1169    }
1170}
1171
1172impl Default for Tuner {
1173    fn default() -> Self { Tuner::new() }
1174}
1175
1176#[cfg(test)]
1177mod body_tests {
1178    use std::mem::size_of;
1179    use super::*;
1180
1181    fn is_send<T: Send>() -> bool { true }
1182    fn is_sync<T: Sync>() -> bool { true }
1183
1184    type Flaw = Box<dyn StdError + Send + Sync + 'static>;
1185    fn is_flaw(_f: Flaw) -> bool { true }
1186
1187    #[test]
1188    fn test_send_sync() {
1189        assert!(is_send::<BodyError>());
1190        assert!(is_sync::<BodyError>());
1191
1192        assert!(is_send::<BodyImage>());
1193        assert!(is_sync::<BodyImage>());
1194
1195        assert!(is_send::<Dialog>());
1196        assert!(is_sync::<Dialog>());
1197
1198        assert!(is_send::<Tunables>());
1199        assert!(is_sync::<Tunables>());
1200
1201        assert!(is_send::<BodyReader<'_>>());
1202        assert!(is_sync::<BodyReader<'_>>());
1203    }
1204
1205    #[test]
1206    fn test_body_error_size() {
1207        assert!(size_of::<BodyError>() <= 24);
1208    }
1209
1210    #[test]
1211    fn test_body_error_as_flaw() {
1212        assert!(is_flaw(BodyError::BodyTooLong(0).into()));
1213    }
1214
1215    #[test]
1216    fn test_empty_read() {
1217        let body = BodyImage::empty();
1218        let mut br = body.reader();
1219        let mut obuf = Vec::new();
1220        br.read_to_end(&mut obuf).unwrap();
1221        assert!(obuf.is_empty());
1222    }
1223
1224    #[test]
1225    fn test_contiguous_read() {
1226        let mut body = BodySink::with_ram_buffers(2);
1227        body.write_all("hello world").unwrap();
1228        let body = body.prepare().unwrap();
1229        let mut br = body.reader();
1230        let mut obuf = String::new();
1231        br.read_to_string(&mut obuf).unwrap();
1232        assert_eq!("hello world", &obuf[..]);
1233    }
1234
1235    #[test]
1236    fn test_scattered_read() {
1237        let mut body = BodySink::with_ram_buffers(2);
1238        body.write_all("hello").unwrap();
1239        body.write_all(" ").unwrap();
1240        body.write_all("world").unwrap();
1241        let body = body.prepare().unwrap();
1242        let mut br = body.reader();
1243        let mut obuf = String::new();
1244        br.read_to_string(&mut obuf).unwrap();
1245        assert_eq!("hello world", &obuf[..]);
1246    }
1247
1248    #[test]
1249    fn test_scattered_read_clone() {
1250        let mut body = BodySink::with_ram_buffers(2);
1251        body.write_all("hello").unwrap();
1252        body.write_all(" ").unwrap();
1253        body.write_all("world").unwrap();
1254        let mut body = body.prepare().unwrap();
1255        let body_clone = body.clone();
1256        body.gather();
1257
1258        let mut br = body.reader();
1259        let mut obuf = String::new();
1260        br.read_to_string(&mut obuf).unwrap();
1261        assert_eq!("hello world", &obuf[..]);
1262
1263        let mut br = body_clone.reader();
1264        let mut obuf = String::new();
1265        br.read_to_string(&mut obuf).unwrap();
1266        assert_eq!("hello world", &obuf[..]);
1267    }
1268
1269    #[test]
1270    fn test_scattered_gather() {
1271        let mut body = BodySink::with_ram_buffers(2);
1272        body.push(&b"hello"[..]).unwrap();
1273        body.push(&b" "[..]).unwrap();
1274        body.push(&b"world"[..]).unwrap();
1275        let mut body = body.prepare().unwrap();
1276        body.gather();
1277        if let BodyReader::Contiguous(cursor) = body.reader() {
1278            let bslice = cursor.into_inner();
1279            assert_eq!(b"hello world", bslice);
1280        } else {
1281            panic!("not contiguous?!");
1282        }
1283    }
1284
1285    #[test]
1286    fn test_read_from() {
1287        let tune = Tunables::new();
1288        let salutation = b"hello world";
1289        let mut src = Cursor::new(salutation);
1290        let body = BodyImage::read_from(&mut src, 0, &tune).unwrap();
1291        let mut br = body.reader();
1292        let mut obuf = Vec::new();
1293        br.read_to_end(&mut obuf).unwrap();
1294        assert_eq!(salutation, &obuf[..]);
1295    }
1296
1297    #[test]
1298    fn test_read_from_too_long() {
1299        let tune = Tuner::new()
1300            .set_max_body_ram(6)
1301            .set_max_body(6)
1302            .finish();
1303        let salutation = b"hello world";
1304        let mut src = Cursor::new(salutation);
1305        if let Err(e) = BodyImage::read_from(&mut src, 0, &tune) {
1306            if let BodyError::BodyTooLong(l) = e {
1307                assert_eq!(l, salutation.len() as u64)
1308            } else {
1309                panic!("Other error: {}", e);
1310            }
1311        } else {
1312            panic!("Read from, too long, success!?");
1313        }
1314    }
1315
1316    #[test]
1317    fn test_fs_read() {
1318        let tune = Tunables::new();
1319        let mut body = BodySink::with_fs(tune.temp_dir()).unwrap();
1320        body.write_all("hello").unwrap();
1321        body.write_all(" ").unwrap();
1322        body.write_all("world").unwrap();
1323        let body = body.prepare().unwrap();
1324        let mut br = body.reader();
1325        let mut obuf = String::new();
1326        br.read_to_string(&mut obuf).unwrap();
1327        assert_eq!("hello world", &obuf[..]);
1328    }
1329
1330    #[test]
1331    fn test_fs_back_read() {
1332        let tune = Tunables::new();
1333        let mut body = BodySink::with_ram_buffers(2);
1334        body.write_all("hello").unwrap();
1335        body.write_all(" ").unwrap();
1336        body.write_all("world").unwrap();
1337        body.write_back(tune.temp_dir()).unwrap();
1338        let body = body.prepare().unwrap();
1339        let mut br = body.reader();
1340        let mut obuf = String::new();
1341        br.read_to_string(&mut obuf).unwrap();
1342        assert_eq!("hello world", &obuf[..]);
1343    }
1344
1345    #[test]
1346    fn test_w_back_w_read() {
1347        let tune = Tunables::new();
1348        let mut body = BodySink::with_ram_buffers(2);
1349        body.write_all("hello").unwrap();
1350        body.write_back(tune.temp_dir()).unwrap();
1351        body.write_all(" ").unwrap();
1352        body.write_all("world").unwrap();
1353        let body = body.prepare().unwrap();
1354        let mut br = body.reader();
1355        let mut obuf = String::new();
1356        br.read_to_string(&mut obuf).unwrap();
1357        assert_eq!("hello world", &obuf[..]);
1358    }
1359
1360    #[test]
1361    fn test_fs_back_fail() {
1362        let tune = Tuner::new().set_temp_dir("./no-existe/").finish();
1363        let mut body = BodySink::with_ram_buffers(2);
1364        body.write_all("hello").unwrap();
1365        body.write_all(" ").unwrap();
1366        body.write_all("world").unwrap();
1367        if body.write_back(tune.temp_dir()).is_err() {
1368            assert!(body.is_ram());
1369            assert_eq!(body.len(), 0);
1370        } else {
1371            panic!("write_back with bogus dir success?!");
1372        }
1373    }
1374
1375    #[cfg(feature = "mmap")]
1376    #[test]
1377    fn test_fs_map_read() {
1378        let tune = Tunables::new();
1379        let mut body = BodySink::with_fs(tune.temp_dir()).unwrap();
1380        body.write_all("hello").unwrap();
1381        body.write_all(" ").unwrap();
1382        body.write_all("world").unwrap();
1383        let mut body = body.prepare().unwrap();
1384        body.mem_map().unwrap();
1385        let mut br = body.reader();
1386        let mut obuf = String::new();
1387        br.read_to_string(&mut obuf).unwrap();
1388        assert_eq!("hello world", &obuf[..]);
1389    }
1390
1391    #[test]
1392    fn test_fs_back_read_clone() {
1393        let tune = Tunables::new();
1394        let mut body = BodySink::with_ram_buffers(2);
1395        body.write_all("hello").unwrap();
1396        body.write_all(" ").unwrap();
1397        body.write_all("world").unwrap();
1398        body.write_back(tune.temp_dir()).unwrap();
1399        let body = body.prepare().unwrap();
1400
1401        {
1402            let mut br = body.reader();
1403            let mut obuf = String::new();
1404            br.read_to_string(&mut obuf).unwrap();
1405            assert_eq!("hello world", &obuf[..]);
1406        }
1407
1408        let body_clone_1 = body.clone();
1409        let body_clone_2 = body_clone_1.clone();
1410
1411        let mut br = body_clone_1.reader();
1412        let mut obuf = String::new();
1413        br.read_to_string(&mut obuf).unwrap();
1414        assert_eq!("hello world", &obuf[..]);
1415
1416        let mut br = body.reader(); // read original (prepared) body
1417        let mut obuf = String::new();
1418        br.read_to_string(&mut obuf).unwrap();
1419        assert_eq!("hello world", &obuf[..]);
1420
1421        let mut br = body_clone_2.reader();
1422        let mut obuf = String::new();
1423        br.read_to_string(&mut obuf).unwrap();
1424        assert_eq!("hello world", &obuf[..]);
1425    }
1426
1427    #[cfg(feature = "mmap")]
1428    #[test]
1429    fn test_fs_map_clone_shared() {
1430        let tune = Tunables::new();
1431        let mut body = BodySink::with_ram_buffers(2);
1432        body.write_all("hello").unwrap();
1433        body.write_all(" ").unwrap();
1434        body.write_all("world").unwrap();
1435        body.write_back(tune.temp_dir()).unwrap();
1436        let mut body = body.prepare().unwrap();
1437        body.mem_map().unwrap();
1438        println!("{:?}", body);
1439
1440        let ptr1 = if let BodyReader::Contiguous(cursor) = body.reader() {
1441            let bslice = cursor.into_inner();
1442            assert_eq!(b"hello world", bslice);
1443            format!("{:p}", bslice)
1444        } else {
1445            panic!("not contiguous?!");
1446        };
1447
1448        let body_clone = body.clone();
1449        println!("{:?}", body_clone);
1450
1451        let ptr2 = if let BodyReader::Contiguous(cursor) = body_clone.reader() {
1452            let bslice = cursor.into_inner();
1453            assert_eq!(b"hello world", bslice);
1454            format!("{:p}", bslice)
1455        } else {
1456            panic!("not contiguous?!");
1457        };
1458
1459        assert_eq!(ptr1, ptr2);
1460    }
1461
1462    #[test]
1463    fn test_fs_empty() {
1464        let tune = Tunables::new();
1465        let body = BodySink::with_fs(tune.temp_dir()).unwrap();
1466        let body = body.prepare().unwrap();
1467        assert!(body.is_empty());
1468    }
1469
1470    #[cfg(feature = "mmap")]
1471    #[test]
1472    fn test_fs_map_empty() {
1473        let tune = Tunables::new();
1474        let body = BodySink::with_fs(tune.temp_dir()).unwrap();
1475        let mut body = body.prepare().unwrap();
1476        body.mem_map().unwrap();
1477        assert!(body.is_empty());
1478    }
1479}