mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-02-23 15:49:49 +01:00
feat(cmn): ContentRange header (parse and format)
Now we are able to send the transfer-update requests and implement the actual chunk logic.
This commit is contained in:
@@ -791,7 +791,7 @@ else {
|
||||
url: url,
|
||||
reader: &mut reader,
|
||||
media_type: reader_mime_type.clone(),
|
||||
content_size: size
|
||||
content_length: size
|
||||
}.upload()
|
||||
};
|
||||
match upload_result {
|
||||
|
||||
109
src/rust/cmn.rs
109
src/rust/cmn.rs
@@ -1,12 +1,15 @@
|
||||
use std::marker::MarkerTrait;
|
||||
use std::io::{self, Read, Seek, Cursor, Write, SeekFrom};
|
||||
use std;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use mime::{Mime, TopLevel, SubLevel, Attr, Value};
|
||||
use oauth2;
|
||||
use oauth2::TokenType;
|
||||
use hyper;
|
||||
use hyper::header::{ContentType, ContentLength, Headers, UserAgent, Authorization};
|
||||
use hyper::header::{ContentType, ContentLength, Headers, UserAgent, Authorization, Header,
|
||||
HeaderFormat};
|
||||
use hyper::http::LINE_ENDING;
|
||||
use hyper::method::Method;
|
||||
|
||||
@@ -361,6 +364,107 @@ impl_header!(XUploadContentType,
|
||||
"X-Upload-Content-Type",
|
||||
Mime);
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum ByteRange {
|
||||
Any,
|
||||
Chunk(u64, u64)
|
||||
}
|
||||
|
||||
impl fmt::Display for ByteRange {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ByteRange::Any => fmt.write_str("*").ok(),
|
||||
ByteRange::Chunk(first, last) => write!(fmt, "{}-{}", first, last).ok()
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ByteRange {
|
||||
type Err = &'static str;
|
||||
|
||||
/// NOTE: only implements `%i-%i`, not `*`
|
||||
fn from_str(s: &str) -> std::result::Result<ByteRange, &'static str> {
|
||||
let parts: Vec<&str> = s.split('-').collect();
|
||||
if parts.len() != 2 {
|
||||
return Err("Expected two parts: %i-%i")
|
||||
}
|
||||
Ok(
|
||||
ByteRange::Chunk(
|
||||
match FromStr::from_str(parts[0]) {
|
||||
Ok(d) => d,
|
||||
_ => return Err("Couldn't parse 'first' as digit")
|
||||
},
|
||||
match FromStr::from_str(parts[1]) {
|
||||
Ok(d) => d,
|
||||
_ => return Err("Couldn't parse 'last' as digit")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the Content-Range header, for serialization only
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct ContentRange {
|
||||
pub range: ByteRange,
|
||||
pub total_length: u64,
|
||||
}
|
||||
|
||||
impl Header for ContentRange {
|
||||
fn header_name() -> &'static str {
|
||||
"Content-Range"
|
||||
}
|
||||
|
||||
/// We are not parsable, as parsing is done by the `Range` header
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<ContentRange> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl HeaderFormat for ContentRange {
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "bytes {}/{}", self.range, self.total_length).ok();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct RangeResponseHeader(pub ByteRange);
|
||||
|
||||
impl Header for RangeResponseHeader {
|
||||
fn header_name() -> &'static str {
|
||||
"Range"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<RangeResponseHeader> {
|
||||
match raw {
|
||||
[ref v] => {
|
||||
if let Ok(s) = std::str::from_utf8(v) {
|
||||
if s.starts_with("bytes=") {
|
||||
return Some(RangeResponseHeader(
|
||||
match FromStr::from_str(&s[6..]) {
|
||||
Ok(br) => br,
|
||||
_ => return None
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderFormat for RangeResponseHeader {
|
||||
/// No implmentation necessary, we just need to parse
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
/// A utility type to perform a resumable upload from start to end.
|
||||
pub struct ResumableUploadHelper<'a, NC: 'a, A: 'a> {
|
||||
pub client: &'a mut hyper::client::Client<NC>,
|
||||
@@ -371,7 +475,7 @@ pub struct ResumableUploadHelper<'a, NC: 'a, A: 'a> {
|
||||
pub url: &'a str,
|
||||
pub reader: &'a mut ReadSeek,
|
||||
pub media_type: Mime,
|
||||
pub content_size: u64
|
||||
pub content_length: u64
|
||||
}
|
||||
|
||||
impl<'a, NC, A> ResumableUploadHelper<'a, NC, A>
|
||||
@@ -381,6 +485,7 @@ impl<'a, NC, A> ResumableUploadHelper<'a, NC, A>
|
||||
fn query_transfer_status(&'a mut self) -> (u64, hyper::HttpResult<hyper::client::Response>) {
|
||||
self.client.post(self.url)
|
||||
.header(UserAgent(self.user_agent.to_string()))
|
||||
.header(ContentRange { range: ByteRange::Any, total_length: self.content_length } )
|
||||
.header(self.auth_header.clone());
|
||||
(0, Err(hyper::error::HttpError::HttpStatusError))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#![allow(dead_code, deprecated, unused_features, unused_variables, unused_imports)]
|
||||
//! library with code shared by all generated implementations
|
||||
#![plugin(serde_macros)]
|
||||
#[macro_use]
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate "yup-oauth2" as oauth2;
|
||||
@@ -19,6 +20,8 @@ mod tests {
|
||||
use std::io::Read;
|
||||
use std::default::Default;
|
||||
use std::old_path::BytesContainer;
|
||||
use hyper;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::json;
|
||||
|
||||
@@ -121,4 +124,35 @@ bar\r\n\
|
||||
// let j = "{\"snooSnoo\":\"foo\",\"foo\":\"bar\"}";
|
||||
// let b: BarOpt = json::from_str(&j).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_range() {
|
||||
for &(ref c, ref expected) in
|
||||
&[(ContentRange {range: ByteRange::Any, total_length: 50 }, "Content-Range: bytes */50\r\n"),
|
||||
(ContentRange {range: ByteRange::Chunk(23, 40), total_length: 45},
|
||||
"Content-Range: bytes 23-40/45\r\n")] {
|
||||
let mut headers = hyper::header::Headers::new();
|
||||
headers.set(c.clone());
|
||||
assert_eq!(headers.to_string(), expected.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_range_from_str() {
|
||||
assert_eq!(<ByteRange as FromStr>::from_str("2-42"),
|
||||
Ok(ByteRange::Chunk(2, 42)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_range_response() {
|
||||
let r: RangeResponseHeader = hyper::header::Header::parse_header(&[b"bytes=2-42".to_vec()]).unwrap();
|
||||
|
||||
match r.0 {
|
||||
ByteRange::Chunk(f, l) => {
|
||||
assert_eq!(f, 2);
|
||||
assert_eq!(l, 42);
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user