#![warn(rust_2018_idioms)]
use std::error::Error as StdError;
use std::env;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
use std::mem;
use std::path::Path;
use std::sync::Arc;
use bytes::{Bytes, BytesMut, BufMut};
use log::{debug, warn};
use olio::io::GatheringReader;
use olio::fs::rc::{ReadPos, ReadSlice};
use tempfile::tempfile_in;
#[cfg(feature = "mmap")] #[doc(hidden)] pub mod _mem_handle_ext;
#[cfg(feature = "mmap")]
use {
memmap::Mmap,
olio::mem::{MemAdvice, MemHandle},
_mem_handle_ext::MemHandleExt,
};
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug)]
pub enum BodyError {
BodyTooLong(u64),
Io(io::Error),
_FutureProof
}
impl fmt::Display for BodyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
BodyError::BodyTooLong(l) =>
write!(
f, "Body length of {}+ bytes exceeds Tunables::max_body",
l
),
BodyError::Io(ref e) =>
write!(f, "Body I/O: {}", e),
BodyError::_FutureProof =>
unreachable!("Don't abuse the _FutureProof!")
}
}
}
impl StdError for BodyError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
BodyError::Io(ref e) => Some(e),
_ => None
}
}
}
impl From<io::Error> for BodyError {
fn from(err: io::Error) -> BodyError {
BodyError::Io(err)
}
}
#[derive(Clone, Debug)]
pub struct BodyImage {
state: ImageState,
len: u64
}
#[derive(Clone)]
enum ImageState {
Ram(Vec<Bytes>),
FsRead(Arc<File>),
FsReadSlice(ReadSlice),
#[cfg(feature = "mmap")]
MemMap(MemHandle<Mmap>),
}
#[derive(Debug)]
pub struct BodySink {
state: SinkState,
len: u64
}
enum SinkState {
Ram(Vec<Bytes>),
FsWrite(File),
}
impl SinkState {
fn cut(&mut self) -> Self {
mem::replace(self, SinkState::Ram(Vec::with_capacity(0)))
}
}
impl BodySink {
pub fn empty() -> BodySink {
BodySink::with_ram_buffers(0)
}
pub fn with_ram(size_estimate: u64) -> BodySink {
if size_estimate == 0 {
BodySink::empty()
} else {
let cap = (size_estimate / 0x2000 + 1) as usize;
BodySink::with_ram_buffers(cap)
}
}
pub fn with_ram_buffers(capacity: usize) -> BodySink {
BodySink {
state: SinkState::Ram(Vec::with_capacity(capacity)),
len: 0
}
}
pub fn with_fs<P>(dir: P) -> Result<BodySink, BodyError>
where P: AsRef<Path>
{
let f = tempfile_in(dir)?;
Ok(BodySink {
state: SinkState::FsWrite(f),
len: 0
})
}
pub fn is_ram(&self) -> bool {
match self.state {
SinkState::Ram(_) => true,
_ => false
}
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn len(&self) -> u64 {
self.len
}
#[deprecated(since="2.0.0", note="use `push` or `write_all` instead")]
#[inline]
pub fn save<T>(&mut self, buf: T) -> Result<(), BodyError>
where T: Into<Bytes>
{
let buf = buf.into();
self.push(buf).map_err(BodyError::from)
}
pub fn push<T>(&mut self, buf: T) -> Result<(), io::Error>
where T: Into<Bytes> + AsRef<[u8]>
{
match self.state {
SinkState::Ram(ref mut v) => {
let buf = buf.into();
let len = buf.len() as u64;
if len > 0 {
v.push(buf);
self.len += len;
}
}
SinkState::FsWrite(ref mut f) => {
let buf = buf.as_ref();
let len = buf.len() as u64;
if len > 0 {
f.write_all(buf)?;
self.len += len;
}
}
}
Ok(())
}
pub fn write_all<T>(&mut self, buf: T) -> Result<(), io::Error>
where T: AsRef<[u8]>
{
let buf = buf.as_ref();
let len = buf.len() as u64;
if len > 0 {
match self.state {
SinkState::Ram(ref mut v) => {
v.push(Bytes::copy_from_slice(buf));
}
SinkState::FsWrite(ref mut f) => {
f.write_all(buf)?;
}
}
self.len += len;
}
Ok(())
}
pub fn write_back<P>(&mut self, dir: P) -> Result<&mut Self, BodyError>
where P: AsRef<Path>
{
if self.is_ram() {
if let SinkState::Ram(v) = self.state.cut() {
let olen = self.len;
self.len = 0;
let mut f = tempfile_in(dir)?;
for b in v {
f.write_all(&b)?;
drop::<Bytes>(b);
}
self.state = SinkState::FsWrite(f);
self.len = olen;
}
}
Ok(self)
}
pub fn prepare(self) -> Result<BodyImage, BodyError> {
match self.state {
SinkState::Ram(v) => {
Ok(BodyImage {
state: ImageState::Ram(v),
len: self.len
})
}
SinkState::FsWrite(mut f) => {
if self.len == 0 {
Ok(BodyImage::empty())
} else {
f.flush()?;
f.seek(SeekFrom::Start(0))?;
Ok(BodyImage {
state: ImageState::FsRead(Arc::new(f)),
len: self.len
})
}
}
}
}
}
impl Default for BodySink {
fn default() -> BodySink { BodySink::empty() }
}
impl fmt::Debug for SinkState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SinkState::Ram(ref v) => {
f.debug_struct("Ram(Vec<Bytes>)")
.field("capacity", &v.capacity())
.field("len", &v.len())
.finish()
}
SinkState::FsWrite(ref file) => {
f.debug_tuple("FsWrite")
.field(file)
.finish()
}
}
}
}
impl ImageState {
fn empty() -> ImageState {
ImageState::Ram(Vec::with_capacity(0))
}
fn cut(&mut self) -> Self {
mem::replace(self, ImageState::empty())
}
}
impl BodyImage {
pub fn empty() -> BodyImage {
BodyImage {
state: ImageState::empty(),
len: 0
}
}
#[cfg(feature = "mmap")]
pub unsafe fn from_file(file: File, length: u64) -> BodyImage {
image_from_file(file, length)
}
#[cfg(not(feature = "mmap"))]
pub fn from_file(file: File, length: u64) -> BodyImage {
image_from_file(file, length)
}
pub fn from_slice<T>(bytes: T) -> BodyImage
where T: Into<Bytes>
{
let mut bs = BodySink::with_ram_buffers(1);
let buf = bytes.into();
bs.push(buf).expect("push is safe to Ram");
bs.prepare().expect("prepare is safe to Ram")
}
#[cfg(feature = "mmap")]
pub unsafe fn from_read_slice(rslice: ReadSlice) -> BodyImage {
image_from_read_slice(rslice)
}
#[cfg(not(feature = "mmap"))]
pub fn from_read_slice(rslice: ReadSlice) -> BodyImage {
image_from_read_slice(rslice)
}
pub fn is_ram(&self) -> bool {
match self.state {
ImageState::Ram(_) => true,
_ => false
}
}
#[cfg(feature = "mmap")]
pub fn is_mem_map(&self) -> bool {
match self.state {
ImageState::MemMap(_) => true,
_ => false
}
}
pub fn len(&self) -> u64 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[cfg(feature = "mmap")]
pub fn mem_map(&mut self) -> Result<&mut Self, BodyError> {
let map = match self.state {
ImageState::FsRead(ref file) => {
assert!(self.len > 0);
unsafe { Mmap::map(&file) }?
}
ImageState::FsReadSlice(ref rslice) => {
rslice.mem_map()?
}
_ => return Ok(self)
};
self.state = ImageState::MemMap(MemHandle::new(map));
Ok(self)
}
pub fn gather(&mut self) -> &mut Self {
let scattered = if let ImageState::Ram(ref v) = self.state {
v.len() > 1
} else {
false
};
if scattered {
if let ImageState::Ram(v) = self.state.cut() {
let mut newb = BytesMut::with_capacity(self.len as usize);
for b in v {
newb.put_slice(&b);
drop::<Bytes>(b);
}
let newb = newb.freeze();
assert_eq!(newb.len() as u64, self.len);
self.state = ImageState::Ram(vec![newb]);
}
}
self
}
pub fn reader(&self) -> BodyReader<'_> {
match self.state {
ImageState::Ram(ref v) => {
if v.is_empty() {
BodyReader::Contiguous(Cursor::new(&[]))
} else if v.len() == 1 {
BodyReader::Contiguous(Cursor::new(&v[0]))
} else {
BodyReader::Scattered(GatheringReader::new(v))
}
}
ImageState::FsRead(ref f) => {
BodyReader::FileSlice(ReadSlice::new(f.clone(), 0, self.len))
}
ImageState::FsReadSlice(ref rslice) => {
BodyReader::FileSlice(rslice.clone())
}
#[cfg(feature = "mmap")]
ImageState::MemMap(ref m) => {
BodyReader::Contiguous(Cursor::new(m))
}
}
}
pub fn explode(self) -> ExplodedImage {
match self.state {
ImageState::Ram(v) => ExplodedImage::Ram(v),
ImageState::FsRead(f) => {
ExplodedImage::FsRead(ReadSlice::new(f, 0, self.len))
}
ImageState::FsReadSlice(rs) => ExplodedImage::FsRead(rs),
#[cfg(feature = "mmap")]
ImageState::MemMap(m) => ExplodedImage::MemMap(m),
}
}
pub fn read_from<R>(rin: &mut R, len_estimate: u64, tune: &Tunables)
-> Result<BodyImage, BodyError>
where R: Read + ?Sized
{
if len_estimate > tune.max_body_ram() {
let b = BodySink::with_fs(tune.temp_dir())?;
return read_to_body_fs(rin, b, tune);
}
let mut body = BodySink::with_ram(len_estimate);
let mut size: u64 = 0;
'eof: loop {
let mut buf = BytesMut::with_capacity(tune.buffer_size_ram());
'fill: loop {
let b = unsafe { &mut *(
buf.chunk_mut() as *mut _
as *mut [mem::MaybeUninit<u8>]
as *mut [u8]
)};
let len = match rin.read(b) {
Ok(len) => len,
Err(e) => {
if e.kind() == ErrorKind::Interrupted {
continue;
} else {
return Err(e.into());
}
}
};
if len == 0 {
break 'fill;
}
unsafe { buf.advance_mut(len); }
if buf.remaining_mut() < 1024 {
break 'fill;
}
}
let len = buf.len() as u64;
if len == 0 {
break 'eof;
}
size += len;
if size > tune.max_body() {
return Err(BodyError::BodyTooLong(size));
}
if size > tune.max_body_ram() {
body.write_back(tune.temp_dir())?;
debug!("Write (Fs) buffer len {}", len);
body.write_all(&buf)?;
return read_to_body_fs(rin, body, tune)
}
debug!("Saved (Ram) buffer len {}", len);
body.push(buf.freeze())?;
}
let body = body.prepare()?;
Ok(body)
}
pub fn write_to<W>(&self, out: &mut W) -> Result<u64, BodyError>
where W: Write + ?Sized
{
match self.state {
ImageState::Ram(ref v) => {
for b in v {
out.write_all(b)?;
}
}
#[cfg(feature = "mmap")]
ImageState::MemMap(ref mh) => {
mh.tmp_advise(MemAdvice::Sequential, || out.write_all(mh))?;
}
ImageState::FsRead(ref f) => {
let mut rp = ReadPos::new(f.clone(), self.len);
io::copy(&mut rp, out)?;
}
ImageState::FsReadSlice(ref rslice) => {
let mut rs = rslice.clone();
io::copy(&mut rs, out)?;
}
}
Ok(self.len)
}
}
fn image_from_file(file: File, length: u64) -> BodyImage {
if length > 0 {
BodyImage {
state: ImageState::FsRead(Arc::new(file)),
len: length
}
} else {
BodyImage::empty()
}
}
fn image_from_read_slice(rslice: ReadSlice) -> BodyImage {
let len = rslice.len();
if len > 0 {
BodyImage { state: ImageState::FsReadSlice(rslice), len }
} else {
BodyImage::empty()
}
}
fn read_to_body_fs<R>(r: &mut R, mut body: BodySink, tune: &Tunables)
-> Result<BodyImage, BodyError>
where R: Read + ?Sized
{
assert!(!body.is_ram());
let mut size: u64 = 0;
let mut buf = BytesMut::with_capacity(tune.buffer_size_fs());
loop {
let b = unsafe { &mut *(
buf.chunk_mut() as *mut _
as *mut [mem::MaybeUninit<u8>]
as *mut [u8]
)};
let len = match r.read(b) {
Ok(l) => l,
Err(e) => {
if e.kind() == ErrorKind::Interrupted {
continue;
} else {
return Err(e.into());
}
}
};
if len == 0 {
break;
}
unsafe { buf.advance_mut(len); }
size += len as u64;
if size > tune.max_body() {
return Err(BodyError::BodyTooLong(size));
}
debug!("Write (Fs) buffer len {}", len);
body.write_all(&buf)?;
buf.clear();
}
let body = body.prepare()?;
Ok(body)
}
impl Default for BodyImage {
fn default() -> BodyImage { BodyImage::empty() }
}
impl fmt::Debug for ImageState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ImageState::Ram(ref v) => {
f.debug_struct("Ram(Vec<Bytes>)")
.field("capacity", &v.capacity())
.field("len", &v.len())
.finish()
}
ImageState::FsRead(ref file) => {
f.debug_tuple("FsRead")
.field(file)
.finish()
}
ImageState::FsReadSlice(ref rslice) => {
f.debug_tuple("FsReadSlice")
.field(rslice)
.finish()
}
#[cfg(feature = "mmap")]
ImageState::MemMap(ref m) => {
f.debug_tuple("MemMap")
.field(m)
.finish()
}
}
}
}
pub enum ExplodedImage {
Ram(Vec<Bytes>),
FsRead(ReadSlice),
#[cfg(feature = "mmap")]
MemMap(MemHandle<Mmap>),
}
pub enum BodyReader<'a> {
Contiguous(Cursor<&'a [u8]>),
Scattered(GatheringReader<'a, Bytes>),
FileSlice(ReadSlice),
}
impl<'a> Read for BodyReader<'a> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
match *self {
BodyReader::Contiguous(ref mut cursor) => cursor.read(buf),
BodyReader::Scattered(ref mut gatherer) => gatherer.read(buf),
BodyReader::FileSlice(ref mut rslice) => rslice.read(buf),
}
}
}
#[derive(Clone, Debug)]
pub struct Prolog {
pub method: http::Method,
pub url: http::Uri,
pub req_headers: http::HeaderMap,
pub req_body: BodyImage,
}
#[derive(Clone, Debug)]
pub struct Epilog {
pub version: http::Version,
pub status: http::StatusCode,
pub res_headers: http::HeaderMap,
pub res_body: BodyImage,
pub res_decoded: Vec<Encoding>,
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum Encoding {
Chunked,
Deflate,
Gzip,
Brotli,
Compress,
Identity,
}
impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Encoding::Chunked => "chunked",
Encoding::Deflate => "deflate",
Encoding::Gzip => "gzip",
Encoding::Brotli => "br",
Encoding::Compress => "compress",
Encoding::Identity => "identity",
})
}
}
#[derive(Clone, Debug)]
pub struct Dialog {
pro: Prolog,
epi: Epilog,
}
pub trait RequestRecorded {
fn req_headers(&self) -> &http::HeaderMap;
fn req_body(&self) -> &BodyImage;
}
pub trait Recorded: RequestRecorded {
fn res_headers(&self) -> &http::HeaderMap;
fn res_body(&self) -> &BodyImage;
}
impl Dialog {
pub fn new(pro: Prolog, epi: Epilog) -> Dialog {
Dialog { pro, epi }
}
pub fn method(&self) -> &http::Method { &self.pro.method }
pub fn url(&self) -> &http::Uri { &self.pro.url }
pub fn req_body_mut(&mut self) -> &mut BodyImage {
&mut self.pro.req_body
}
pub fn res_status(&self) -> http::StatusCode { self.epi.status }
pub fn res_version(&self) -> http::Version { self.epi.version }
pub fn res_decoded(&self) -> &Vec<Encoding> { &self.epi.res_decoded }
pub fn set_res_decoded(&mut self, decoded: Vec<Encoding>) {
self.epi.res_decoded = decoded;
}
pub fn res_body_mut(&mut self) -> &mut BodyImage { &mut self.epi.res_body }
pub fn set_res_body_decoded(&mut self, body: BodyImage, decoded: Vec<Encoding>) {
self.epi.res_body = body;
self.epi.res_decoded = decoded;
}
pub fn explode(self) -> (Prolog, Epilog) {
(self.pro, self.epi)
}
}
impl RequestRecorded for Dialog {
fn req_headers(&self) -> &http::HeaderMap { &self.pro.req_headers }
fn req_body(&self) -> &BodyImage { &self.pro.req_body }
}
impl Recorded for Dialog {
fn res_headers(&self) -> &http::HeaderMap { &self.epi.res_headers }
fn res_body(&self) -> &BodyImage { &self.epi.res_body }
}
#[derive(Debug, Clone)]
pub struct Tunables {
max_body_ram: u64,
max_body: u64,
buffer_size_ram: usize,
buffer_size_fs: usize,
size_estimate_deflate: u16,
size_estimate_gzip: u16,
size_estimate_brotli: u16,
temp_dir: Arc<Path>,
}
impl Tunables {
pub fn new() -> Tunables {
Tunables {
max_body_ram: 192 * 1024,
max_body: 1024 * 1024 * 1024,
buffer_size_ram: 8 * 1024,
buffer_size_fs: 64 * 1024,
size_estimate_deflate: 4,
size_estimate_gzip: 5,
size_estimate_brotli: 6,
temp_dir: env::temp_dir().into(),
}
}
pub fn max_body_ram(&self) -> u64 {
self.max_body_ram
}
pub fn max_body(&self) -> u64 {
self.max_body
}
pub fn buffer_size_ram(&self) -> usize {
self.buffer_size_ram
}
pub fn buffer_size_fs(&self) -> usize {
self.buffer_size_fs
}
pub fn size_estimate_deflate(&self) -> u16 {
self.size_estimate_deflate
}
pub fn size_estimate_gzip(&self) -> u16 {
self.size_estimate_gzip
}
pub fn size_estimate_brotli(&self) -> u16 {
self.size_estimate_brotli
}
pub fn temp_dir(&self) -> &Path {
&self.temp_dir
}
pub fn temp_dir_rc(&self) -> Arc<Path> {
self.temp_dir.clone()
}
}
impl Default for Tunables {
fn default() -> Self { Tunables::new() }
}
#[derive(Clone)]
pub struct Tuner {
template: Tunables
}
impl Tuner {
pub fn new() -> Tuner {
Tuner { template: Tunables::new() }
}
pub fn set_max_body_ram(&mut self, size: u64) -> &mut Tuner {
self.template.max_body_ram = size;
self
}
pub fn set_max_body(&mut self, size: u64) -> &mut Tuner {
self.template.max_body = size;
self
}
pub fn set_buffer_size_ram(&mut self, size: usize) -> &mut Tuner {
assert!(size > 0, "buffer_size_ram must be greater than zero");
self.template.buffer_size_ram = size;
self
}
pub fn set_buffer_size_fs(&mut self, size: usize) -> &mut Tuner {
assert!(size > 0, "buffer_size_fs must be greater than zero");
self.template.buffer_size_fs = size;
self
}
pub fn set_size_estimate_deflate(&mut self, multiple: u16) -> &mut Tuner {
assert!(multiple > 0, "size_estimate_deflate must be >= 1");
self.template.size_estimate_deflate = multiple;
self
}
pub fn set_size_estimate_gzip(&mut self, multiple: u16) -> &mut Tuner {
assert!(multiple > 0, "size_estimate_gzip must be >= 1");
self.template.size_estimate_gzip = multiple;
self
}
pub fn set_size_estimate_brotli(&mut self, multiple: u16) -> &mut Tuner {
assert!(multiple > 0, "size_estimate_brotli must be >= 1");
self.template.size_estimate_brotli = multiple;
self
}
pub fn set_temp_dir<P>(&mut self, path: P) -> &mut Tuner
where P: AsRef<Path>
{
self.template.temp_dir = path.as_ref().into();
self
}
pub fn finish(&self) -> Tunables {
let t = self.template.clone();
assert!(t.max_body_ram <= t.max_body,
"max_body_ram can't be greater than max_body");
t
}
}
impl Default for Tuner {
fn default() -> Self { Tuner::new() }
}
#[cfg(test)]
mod body_tests {
use std::mem::size_of;
use super::*;
fn is_send<T: Send>() -> bool { true }
fn is_sync<T: Sync>() -> bool { true }
type Flaw = Box<dyn StdError + Send + Sync + 'static>;
fn is_flaw(_f: Flaw) -> bool { true }
#[test]
fn test_send_sync() {
assert!(is_send::<BodyError>());
assert!(is_sync::<BodyError>());
assert!(is_send::<BodyImage>());
assert!(is_sync::<BodyImage>());
assert!(is_send::<Dialog>());
assert!(is_sync::<Dialog>());
assert!(is_send::<Tunables>());
assert!(is_sync::<Tunables>());
assert!(is_send::<BodyReader<'_>>());
assert!(is_sync::<BodyReader<'_>>());
}
#[test]
fn test_body_error_size() {
assert!(size_of::<BodyError>() <= 24);
}
#[test]
fn test_body_error_as_flaw() {
assert!(is_flaw(BodyError::BodyTooLong(0).into()));
}
#[test]
fn test_empty_read() {
let body = BodyImage::empty();
let mut br = body.reader();
let mut obuf = Vec::new();
br.read_to_end(&mut obuf).unwrap();
assert!(obuf.is_empty());
}
#[test]
fn test_contiguous_read() {
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello world").unwrap();
let body = body.prepare().unwrap();
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_scattered_read() {
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
let body = body.prepare().unwrap();
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_scattered_read_clone() {
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
let mut body = body.prepare().unwrap();
let body_clone = body.clone();
body.gather();
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
let mut br = body_clone.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_scattered_gather() {
let mut body = BodySink::with_ram_buffers(2);
body.push(&b"hello"[..]).unwrap();
body.push(&b" "[..]).unwrap();
body.push(&b"world"[..]).unwrap();
let mut body = body.prepare().unwrap();
body.gather();
if let BodyReader::Contiguous(cursor) = body.reader() {
let bslice = cursor.into_inner();
assert_eq!(b"hello world", bslice);
} else {
panic!("not contiguous?!");
}
}
#[test]
fn test_read_from() {
let tune = Tunables::new();
let salutation = b"hello world";
let mut src = Cursor::new(salutation);
let body = BodyImage::read_from(&mut src, 0, &tune).unwrap();
let mut br = body.reader();
let mut obuf = Vec::new();
br.read_to_end(&mut obuf).unwrap();
assert_eq!(salutation, &obuf[..]);
}
#[test]
fn test_read_from_too_long() {
let tune = Tuner::new()
.set_max_body_ram(6)
.set_max_body(6)
.finish();
let salutation = b"hello world";
let mut src = Cursor::new(salutation);
if let Err(e) = BodyImage::read_from(&mut src, 0, &tune) {
if let BodyError::BodyTooLong(l) = e {
assert_eq!(l, salutation.len() as u64)
} else {
panic!("Other error: {}", e);
}
} else {
panic!("Read from, too long, success!?");
}
}
#[test]
fn test_fs_read() {
let tune = Tunables::new();
let mut body = BodySink::with_fs(tune.temp_dir()).unwrap();
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
let body = body.prepare().unwrap();
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_fs_back_read() {
let tune = Tunables::new();
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
body.write_back(tune.temp_dir()).unwrap();
let body = body.prepare().unwrap();
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_w_back_w_read() {
let tune = Tunables::new();
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello").unwrap();
body.write_back(tune.temp_dir()).unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
let body = body.prepare().unwrap();
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_fs_back_fail() {
let tune = Tuner::new().set_temp_dir("./no-existe/").finish();
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
if body.write_back(tune.temp_dir()).is_err() {
assert!(body.is_ram());
assert_eq!(body.len(), 0);
} else {
panic!("write_back with bogus dir success?!");
}
}
#[cfg(feature = "mmap")]
#[test]
fn test_fs_map_read() {
let tune = Tunables::new();
let mut body = BodySink::with_fs(tune.temp_dir()).unwrap();
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
let mut body = body.prepare().unwrap();
body.mem_map().unwrap();
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_fs_back_read_clone() {
let tune = Tunables::new();
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
body.write_back(tune.temp_dir()).unwrap();
let body = body.prepare().unwrap();
{
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
let body_clone_1 = body.clone();
let body_clone_2 = body_clone_1.clone();
let mut br = body_clone_1.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
let mut br = body.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
let mut br = body_clone_2.reader();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[cfg(feature = "mmap")]
#[test]
fn test_fs_map_clone_shared() {
let tune = Tunables::new();
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello").unwrap();
body.write_all(" ").unwrap();
body.write_all("world").unwrap();
body.write_back(tune.temp_dir()).unwrap();
let mut body = body.prepare().unwrap();
body.mem_map().unwrap();
println!("{:?}", body);
let ptr1 = if let BodyReader::Contiguous(cursor) = body.reader() {
let bslice = cursor.into_inner();
assert_eq!(b"hello world", bslice);
format!("{:p}", bslice)
} else {
panic!("not contiguous?!");
};
let body_clone = body.clone();
println!("{:?}", body_clone);
let ptr2 = if let BodyReader::Contiguous(cursor) = body_clone.reader() {
let bslice = cursor.into_inner();
assert_eq!(b"hello world", bslice);
format!("{:p}", bslice)
} else {
panic!("not contiguous?!");
};
assert_eq!(ptr1, ptr2);
}
#[test]
fn test_fs_empty() {
let tune = Tunables::new();
let body = BodySink::with_fs(tune.temp_dir()).unwrap();
let body = body.prepare().unwrap();
assert!(body.is_empty());
}
#[cfg(feature = "mmap")]
#[test]
fn test_fs_map_empty() {
let tune = Tunables::new();
let body = BodySink::with_fs(tune.temp_dir()).unwrap();
let mut body = body.prepare().unwrap();
body.mem_map().unwrap();
assert!(body.is_empty());
}
}