Skip to content

Commit

Permalink
feat(common): MultiPartReader is working.
Browse files Browse the repository at this point in the history
Something that is missing is a single-byte read test
  • Loading branch information
Byron committed Mar 18, 2015
1 parent 71c827b commit 8db346b
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 5 deletions.
37 changes: 34 additions & 3 deletions src/rust/cmn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ const BOUNDARY: &'static str = "MDuXWGyeE33QFXGchb2VFWc4Z7945d";
pub struct MultiPartReader<'a> {
raw_parts: Vec<(Headers, &'a mut Read)>,
current_part: Option<(Cursor<Vec<u8>>, &'a mut Read)>,
last_part_boundary: Option<Cursor<Vec<u8>>>,
}

impl<'a> MultiPartReader<'a> {
Expand Down Expand Up @@ -163,16 +164,33 @@ impl<'a> MultiPartReader<'a> {
vec![(Attr::Ext("boundary".to_string()), Value::Ext(BOUNDARY.to_string()))],
)
}

/// Returns true if we are totally used
fn is_depleted(&self) -> bool {
self.raw_parts.len() == 0 && self.current_part.is_none()
}

/// Returns true if we are handling our last part
fn is_last_part(&self) -> bool {
self.raw_parts.len() == 0 && self.current_part.is_some()
}
}

impl<'a> Read for MultiPartReader<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.raw_parts.len() == 0 && self.current_part.is_none() {
if self.last_part_boundary.is_some() {
let br = self.last_part_boundary.as_mut().unwrap().read(buf).unwrap_or(0);
if br < buf.len() {
self.last_part_boundary = None;
}
return Ok(br)
} else if self.is_depleted() {
return Ok(0)
} else if self.raw_parts.len() > 0 && self.current_part.is_none() {
let (headers, reader) = self.raw_parts.remove(0);
let mut c = Cursor::new(Vec::<u8>::new());
write!(&mut c, "{}{}", headers, LINE_ENDING).unwrap();
write!(&mut c, "{}--{}{}{}{}", LINE_ENDING, BOUNDARY, LINE_ENDING,
headers, LINE_ENDING).unwrap();
c.seek(SeekFrom::Start(0)).unwrap();
self.current_part = Some((c, reader));
}
Expand All @@ -186,10 +204,23 @@ impl<'a> Read for MultiPartReader<'a> {
match rr {
Ok(bytes_read) => {
if bytes_read == 0 {
if self.is_last_part() {
// before clearing the last part, we will add the boundary that
// will be written last
self.last_part_boundary = Some(Cursor::new(
format!("{}--{}", LINE_ENDING, BOUNDARY).into_bytes()))
}
// We are depleted - this can trigger the next part to come in
self.current_part = None;
}
Ok(hb + bytes_read)
let mut total_bytes_read = hb + bytes_read;
while total_bytes_read < buf.len() && !self.is_depleted() {
match self.read(&mut buf[total_bytes_read ..]) {
Ok(br) => total_bytes_read += br,
Err(err) => return Err(err),
}
}
Ok(total_bytes_read)
}
Err(err) => {
// fail permanently
Expand Down
23 changes: 21 additions & 2 deletions src/rust/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,27 @@ mod tests {
.add_part(&mut r2, 25, &"application/plain".parse().unwrap());

let mut res = String::new();
assert_eq!(mpr.read_to_string(&mut res), Ok(57));
let r = mpr.read_to_string(&mut res);
assert_eq!(res.len(), r.clone().unwrap());
println!("{}", res);
assert!(false);

let expected =
"\r\n--MDuXWGyeE33QFXGchb2VFWc4Z7945d\r\n\
Content-Length: 50\r\n\
Content-Type: application/json\r\n\
\r\n\
foo\r\n\
--MDuXWGyeE33QFXGchb2VFWc4Z7945d\r\n\
Content-Length: 25\r\n\
Content-Type: application/plain\r\n\
\r\n\
bar\r\n\
--MDuXWGyeE33QFXGchb2VFWc4Z7945d";
// NOTE: This CAN fail, as the underlying header hashmap is not sorted
// As the test is just for dev, and doesn't run on travis, we are fine,
// for now. Possible solution would be to omit the size field (make it
// optional)
assert_eq!(res, expected);
assert_eq!(r, Ok(221));
}
}

0 comments on commit 8db346b

Please sign in to comment.