use std::io::{Write, Read, copy};
use hyper::{self, Client, Url};
use hyper::client::Body;
use hyper::client::request::Request;
use hyper::header::{Headers,ContentLength,ContentType};
use hyper::mime::Mime;
use hyper::method::Method;
use hyper::net::{Streaming, NetworkConnector, NetworkStream};
use serde::Deserialize;
use serde_json;
use B2Error;
use B2AuthHeader;
use raw::authorize::B2Authorization;
use raw::files::MoreFileInfo;
#[derive(Deserialize,Serialize,Clone,Debug)]
#[serde(rename_all = "camelCase")]
pub struct UploadAuthorization {
pub bucket_id: String,
pub upload_url: String,
pub authorization_token: String
}
impl UploadAuthorization {
pub fn auth_header(&self) -> B2AuthHeader {
B2AuthHeader(self.authorization_token.clone())
}
}
impl B2Authorization {
pub fn get_upload_url(&self, bucket_id: &str, client: &Client)
-> Result<UploadAuthorization,B2Error>
{
let url_string: String = format!("{}/b2api/v1/b2_get_upload_url", self.api_url);
let url: &str = &url_string;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Request<'a> {
bucket_id: &'a str
}
let request = Request {
bucket_id: bucket_id
};
let body: String = serde_json::to_string(&request)?;
let resp = client.post(url)
.body(Body::BufBody(body.as_bytes(), body.len()))
.header(self.auth_header())
.send()?;
if resp.status != hyper::status::StatusCode::Ok {
Err(B2Error::from_response(resp))
} else {
Ok(serde_json::from_reader(resp)?)
}
}
}
impl UploadAuthorization {
pub fn upload_file<InfoType, R: Read, C, S>(&self, file: &mut R, file_name: String, content_type: Option<Mime>,
content_length: u64, content_sha1: String, connector: &C)
-> Result<MoreFileInfo<InfoType>, B2Error>
where for<'de> InfoType: Deserialize<'de>, R: Sized, C: NetworkConnector<Stream=S>,
S: Into<Box<NetworkStream + Send>>
{
let mut ufr = self.create_upload_file_request(
file_name, content_type, content_length, content_sha1, connector)?;
copy(file, &mut ufr)?;
ufr.finish()
}
pub fn create_upload_file_request<C,S>(&self, file_name: String,
content_type: Option<Mime>,
content_length: u64, content_sha1: String,
connector: &C)
-> Result<UploadFileRequest, B2Error>
where C: NetworkConnector<Stream=S>, S: Into<Box<NetworkStream + Send>>
{
let url: Url = Url::parse(&self.upload_url)?;
let mut request = Request::with_connector(Method::Post, url, connector)?;
{
let headers: &mut Headers = request.headers_mut();
headers.set(self.auth_header());
headers.set(XBzFileName(file_name));
headers.set(XBzContentSha1(content_sha1));
headers.set(ContentLength(content_length));
headers.set(ContentType(match content_type {
Some(v) => v,
None => "b2/x-auto".parse().unwrap()
}));
}
Ok(UploadFileRequest { request: request.start()? })
}
pub fn create_upload_file_request_sha1_at_end<C,S>(&self, file_name: String,
content_type: Option<Mime>,
content_length: u64,
connector: &C)
-> Result<UploadFileRequestSha1End, B2Error>
where C: NetworkConnector<Stream=S>, S: Into<Box<NetworkStream + Send>>
{
let url: Url = Url::parse(&self.upload_url)?;
let mut request = Request::with_connector(Method::Post, url, connector)?;
{
let headers: &mut Headers = request.headers_mut();
headers.set(self.auth_header());
headers.set(XBzFileName(file_name));
headers.set(XBzContentSha1("hex_digits_at_end".to_owned()));
headers.set(ContentLength(content_length + 40));
headers.set(ContentType(match content_type {
Some(v) => v,
None => "b2/x-auto".parse().unwrap()
}));
}
Ok(UploadFileRequestSha1End { request: request.start()? })
}
}
header! { (XBzFileName, "X-Bz-File-Name") => [String] }
header! { (XBzContentSha1, "X-Bz-Content-Sha1") => [String] }
pub struct UploadFileRequest {
request: Request<Streaming>
}
impl Write for UploadFileRequest {
fn write(&mut self, msg: &[u8]) -> ::std::io::Result<usize> {
self.request.write(msg)
}
fn flush(&mut self) -> ::std::io::Result<()> {
self.request.flush()
}
fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> {
self.request.write_all(buf)
}
fn write_fmt(&mut self, fmt: ::core::fmt::Arguments) -> Result<(), ::std::io::Error> {
self.request.write_fmt(fmt)
}
}
impl UploadFileRequest {
pub fn finish<InfoType>(self) -> Result<MoreFileInfo<InfoType>, B2Error>
where for<'de> InfoType: Deserialize<'de>
{
let resp = self.request.send()?;
if resp.status != hyper::status::StatusCode::Ok {
Err(B2Error::from_response(resp))
} else {
Ok(serde_json::from_reader(resp)?)
}
}
}
pub struct UploadFileRequestSha1End {
request: Request<Streaming>
}
impl Write for UploadFileRequestSha1End {
fn write(&mut self, msg: &[u8]) -> ::std::io::Result<usize> {
self.request.write(msg)
}
fn flush(&mut self) -> ::std::io::Result<()> {
self.request.flush()
}
fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> {
self.request.write_all(buf)
}
fn write_fmt(&mut self, fmt: ::core::fmt::Arguments) -> Result<(), ::std::io::Error> {
self.request.write_fmt(fmt)
}
}
impl UploadFileRequestSha1End {
pub fn finish<InfoType>(mut self, sha1: &str) -> Result<MoreFileInfo<InfoType>, B2Error>
where for<'de> InfoType: Deserialize<'de>
{
self.request.write_all(sha1.as_bytes())?;
let resp = self.request.send()?;
if resp.status != hyper::status::StatusCode::Ok {
Err(B2Error::from_response(resp))
} else {
Ok(serde_json::from_reader(resp)?)
}
}
}