#[cfg(feature = "brotli")] extern crate brotli;
extern crate bytes;
#[macro_use] extern crate failure;
extern crate flate2;
extern crate http;
extern crate httparse;
#[cfg(all(test, feature = "client"))]
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
extern crate olio;
#[cfg(feature = "mmap")] extern crate memmap;
extern crate tempfile;
pub mod barc;
#[cfg(feature = "client")] pub mod client;
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, PathBuf};
use std::sync::Arc;
use std::time::Duration;
use bytes::{Bytes, BytesMut, BufMut};
use olio::io::GatheringReader;
use olio::fs::rc::{ReadPos, ReadSlice};
#[cfg(feature = "mmap")]
use memmap::{Mmap};
use tempfile::tempfile_in;
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Fail, Debug)]
pub enum BodyError {
#[fail(display = "Body length of {}+ bytes exceeds Tunables::max_body",
_0)]
BodyTooLong(u64),
#[fail(display = "{}", _0)]
Io(#[cause] io::Error),
#[fail(display = "The future!")]
_FutureProof,
}
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(Arc<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
}
pub fn save<T>(&mut self, buf: T) -> Result<(), BodyError>
where T: Into<Bytes>
{
let buf = buf.into();
let len = buf.len() as u64;
if len > 0 {
match self.state {
SinkState::Ram(ref mut v) => {
v.push(buf);
}
SinkState::FsWrite(ref mut f) => {
f.write_all(&buf)?;
}
}
self.len += len;
}
Ok(())
}
pub fn write_all<T>(&mut self, buf: T) -> Result<(), BodyError>
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(buf.into());
}
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
}
}
pub fn from_file(file: File, length: u64) -> BodyImage {
if length > 0 {
BodyImage {
state: ImageState::FsRead(Arc::new(file)),
len: length
}
} else {
BodyImage::empty()
}
}
pub fn from_slice<T>(bytes: T) -> BodyImage
where T: Into<Bytes>
{
let mut bs = BodySink::with_ram_buffers(1);
bs.save(bytes).expect("safe for Ram");
bs.prepare().expect("safe for Ram")
}
pub fn from_read_slice(rslice: ReadSlice) -> BodyImage {
let len = rslice.len();
if len > 0 {
BodyImage { state: ImageState::FsReadSlice(rslice), len }
} else {
BodyImage::empty()
}
}
pub fn is_ram(&self) -> bool {
match self.state {
ImageState::Ram(_) => 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(Arc::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::File(ReadPos::new(f.clone(), self.len))
}
ImageState::FsReadSlice(ref rslice) => {
BodyReader::FileSlice(rslice.clone())
}
#[cfg(feature = "mmap")]
ImageState::MemMap(ref m) => {
BodyReader::Contiguous(Cursor::new(m))
}
}
}
pub fn read_from(r: &mut Read, len_estimate: u64, tune: &Tunables)
-> Result<BodyImage, BodyError>
{
if len_estimate > tune.max_body_ram() {
let b = BodySink::with_fs(tune.temp_dir())?;
return read_to_body_fs(r, 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 len = match r.read(unsafe { buf.bytes_mut() }) {
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(r, body, tune)
}
debug!("Saved (Ram) buffer len {}", len);
body.save(buf.freeze())?;
}
let body = body.prepare()?;
Ok(body)
}
pub fn write_to(&self, out: &mut Write) -> Result<u64, BodyError> {
match self.state {
ImageState::Ram(ref v) => {
for b in v {
out.write_all(b)?;
}
}
#[cfg(feature = "mmap")]
ImageState::MemMap(ref m) => {
out.write_all(m)?;
}
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 read_to_body_fs(r: &mut Read, mut body: BodySink, tune: &Tunables)
-> Result<BodyImage, BodyError>
{
assert!(!body.is_ram());
let mut size: u64 = 0;
let mut buf = BytesMut::with_capacity(tune.buffer_size_fs());
loop {
let len = match r.read(unsafe { buf.bytes_mut() }) {
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 BodyReader<'a> {
Contiguous(Cursor<&'a [u8]>),
Scattered(GatheringReader<'a, Bytes>),
File(ReadPos),
FileSlice(ReadSlice),
}
impl<'a> BodyReader<'a> {
pub fn as_read(&mut self) -> &mut Read {
match *self {
BodyReader::Contiguous(ref mut cursor) => cursor,
BodyReader::Scattered(ref mut gatherer) => gatherer,
BodyReader::File(ref mut rpos) => rpos,
BodyReader::FileSlice(ref mut rslice) => rslice,
}
}
}
#[derive(Clone, Debug)]
struct Prolog {
method: http::Method,
url: http::Uri,
req_headers: http::HeaderMap,
req_body: BodyImage,
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum Encoding {
Chunked,
Deflate,
Gzip,
Brotli,
}
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",
})
}
}
#[derive(Clone, Debug)]
pub struct Dialog {
prolog: Prolog,
version: http::Version,
status: http::StatusCode,
res_decoded: Vec<Encoding>,
res_headers: http::HeaderMap,
res_body: BodyImage,
}
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 method(&self) -> &http::Method { &self.prolog.method }
pub fn url(&self) -> &http::Uri { &self.prolog.url }
pub fn req_body_mut(&mut self) -> &mut BodyImage {
&mut self.prolog.req_body
}
pub fn res_status(&self) -> http::StatusCode { self.status }
pub fn res_version(&self) -> http::Version { self.version }
pub fn res_decoded(&self) -> &Vec<Encoding> { &self.res_decoded }
pub fn res_body_mut(&mut self) -> &mut BodyImage { &mut self.res_body }
}
impl RequestRecorded for Dialog {
fn req_headers(&self) -> &http::HeaderMap { &self.prolog.req_headers }
fn req_body(&self) -> &BodyImage { &self.prolog.req_body }
}
impl Recorded for Dialog {
fn res_headers(&self) -> &http::HeaderMap { &self.res_headers }
fn res_body(&self) -> &BodyImage { &self.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: PathBuf,
res_timeout: Duration,
body_timeout: Duration,
}
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(),
res_timeout: Duration::from_secs(20),
body_timeout: Duration::from_secs(60),
}
}
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 res_timeout(&self) -> Duration {
self.res_timeout
}
pub fn body_timeout(&self) -> Duration {
self.body_timeout
}
}
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 set_res_timeout(&mut self, dur: Duration) -> &mut Tuner
{
self.template.res_timeout = dur;
self
}
pub fn set_body_timeout(&mut self, dur: Duration) -> &mut Tuner
{
self.template.body_timeout = dur;
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");
assert!(t.res_timeout <= t.body_timeout,
"res_timeout can't be greater than body_timeout");
t
}
}
impl Default for Tuner {
fn default() -> Self { Tuner::new() }
}
#[cfg(test)]
mod root {
use super::*;
fn is_send<T: Send>() -> bool { true }
fn is_sync<T: Sync>() -> bool { true }
#[test]
fn test_send_sync() {
assert!(is_send::<BodyImage>());
assert!(is_sync::<BodyImage>());
assert!(is_send::<Tunables>());
assert!(is_sync::<Tunables>());
assert!(is_send::<Dialog>());
assert!(is_sync::<Dialog>());
assert!(is_send::<BodyReader>());
assert!(is_sync::<BodyReader>());
}
#[test]
fn test_body_empty_read() {
let body = BodyImage::empty();
let mut body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = Vec::new();
br.read_to_end(&mut obuf).unwrap();
assert!(obuf.is_empty());
}
#[test]
fn test_body_contiguous_read() {
let mut body = BodySink::with_ram_buffers(2);
body.write_all("hello world").unwrap();
let body = body.prepare().unwrap();
let mut body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_body_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 body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_body_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 body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
let mut body_reader = body_clone.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_body_scattered_gather() {
let mut body = BodySink::with_ram_buffers(2);
body.save(&b"hello"[..]).unwrap();
body.save(&b" "[..]).unwrap();
body.save(&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_body_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 body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = Vec::new();
br.read_to_end(&mut obuf).unwrap();
assert_eq!(salutation, &obuf[..]);
}
#[test]
fn test_body_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_body_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 body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_body_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 body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_body_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 body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_body_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 let Err(_) = body.write_back(tune.temp_dir()) {
assert!(body.is_ram());
assert_eq!(body.len(), 0);
} else {
panic!("write_back with bogus dir success?!");
}
}
#[cfg(feature = "mmap")]
#[test]
fn test_body_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 body_reader = body.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[test]
fn test_body_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 body_reader = body.reader();
let br = body_reader.as_read();
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 body_reader = body_clone_1.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
let mut body_reader = body.reader(); let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
let mut body_reader = body_clone_2.reader();
let br = body_reader.as_read();
let mut obuf = String::new();
br.read_to_string(&mut obuf).unwrap();
assert_eq!("hello world", &obuf[..]);
}
#[cfg(feature = "mmap")]
#[test]
fn test_body_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_body_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_body_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());
}
}