diff --git a/Cargo.toml b/Cargo.toml index ede7d6cd36..c4d9e3b187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "azure_sdk_for_rust" -version = "0.10.0" +version = "0.10.1" description = "Rust wrappers around Microsoft Azure REST APIs" readme = "README.md" authors = ["Francesco Cogno ", "Max Gortman ", "Dong Liu "] @@ -41,3 +41,4 @@ tokio-core = "0.1" [features] test_e2e = [] +emulator = [] diff --git a/examples/container02.rs b/examples/container02.rs new file mode 100644 index 0000000000..00bb51d2c0 --- /dev/null +++ b/examples/container02.rs @@ -0,0 +1,82 @@ +extern crate azure_sdk_for_rust; +extern crate chrono; +extern crate env_logger; +extern crate futures; +extern crate hyper; +extern crate hyper_tls; +extern crate log; +extern crate md5; +extern crate tokio_core; + +use azure_sdk_for_rust::prelude::*; +use azure_sdk_for_rust::storage::container::PublicAccess; +use futures::future::*; +use std::error::Error; +use tokio_core::reactor::Core; + +fn main() { + env_logger::init(); + code().unwrap(); +} + +// We run a separate method to use the elegant quotation mark operator. +// A series of unwrap(), unwrap() would have achieved the same result. +fn code() -> Result<(), Box> { + let mut core = Core::new()?; + + // this will only work with the emulator + let client = Client::new("", "")?; + + // create container + let future = client + .create_container() + .with_container_name("emulcont") + .with_public_access(PublicAccess::None) + .finalize(); + core.run(future.map(|res| println!("{:?}", res)))?; + + //let data = b"something"; + + //// this is not mandatory but it helps preventing + //// spurious data to be uploaded. + //let digest = md5::compute(&data[..]); + + //let future = client + // .put_block_blob() + // .with_container_name(&container_name) + // .with_blob_name("blob0.txt") + // .with_content_type("text/plain") + // .with_body(&data[..]) + // .with_content_md5(&digest[..]) + // .finalize(); + //core.run(future.map(|res| println!("{:?}", res)))?; + + //let future = client + // .put_block_blob() + // .with_container_name(&container_name) + // .with_blob_name("blob1.txt") + // .with_content_type("text/plain") + // .with_body(&data[..]) + // .with_content_md5(&digest[..]) + // .finalize(); + //core.run(future.map(|res| println!("{:?}", res)))?; + + //let future = client + // .put_block_blob() + // .with_container_name(&container_name) + // .with_blob_name("blob2.txt") + // .with_content_type("text/plain") + // .with_body(&data[..]) + // .with_content_md5(&digest[..]) + // .finalize(); + //core.run(future.map(|res| println!("{:?}", res)))?; + + //let future = client + // .list_blobs() + // .with_container_name(&container_name) + // .with_include_metadata() + // .finalize(); + //core.run(future.map(|res| println!("{:?}", res)))?; + + Ok(()) +} diff --git a/src/azure/core/parsing.rs b/src/azure/core/parsing.rs index ed7e8d2bd1..bd142b249a 100644 --- a/src/azure/core/parsing.rs +++ b/src/azure/core/parsing.rs @@ -119,10 +119,12 @@ pub fn find_subnodes<'a>(node: &'a Element, subnode: &str) -> Vec<&'a Element> { .filter(|x| match **x { ElementNode(ref mynode) => mynode.name == subnode, _ => false, - }).map(|x| match *x { + }) + .map(|x| match *x { ElementNode(ref mynode) => mynode, _ => unreachable!(), - }).collect::>() + }) + .collect::>() } #[inline] diff --git a/src/azure/storage/blob/mod.rs b/src/azure/storage/blob/mod.rs index f8c3ebb3d3..b80baef72b 100644 --- a/src/azure/storage/blob/mod.rs +++ b/src/azure/storage/blob/mod.rs @@ -224,7 +224,8 @@ impl Blob { .ok_or_else(|| { static CL: header::HeaderName = header::CONTENT_LENGTH; AzureError::HeaderNotFound(CL.as_str().to_owned()) - })?.to_str()? + })? + .to_str()? .parse::()?; trace!("content_length == {:?}", content_length); @@ -370,15 +371,15 @@ where { match params { Some(ref params) => format!( - "https://{}.blob.core.windows.net/{}/{}?{}", - t.client().account(), + "{}/{}/{}?{}", + t.client().blob_uri(), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), utf8_percent_encode(t.blob_name(), COMPLETE_ENCODE_SET), params ), None => format!( - "https://{}.blob.core.windows.net/{}/{}", - t.client().account(), + "{}/{}/{}", + t.client().blob_uri(), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), utf8_percent_encode(t.blob_name(), COMPLETE_ENCODE_SET) ), diff --git a/src/azure/storage/client.rs b/src/azure/storage/client.rs index 703a070781..77fb0e1041 100644 --- a/src/azure/storage/client.rs +++ b/src/azure/storage/client.rs @@ -6,10 +6,6 @@ use hyper::{self, Method}; use hyper_tls; use std::borrow::Borrow; -// Can be variant for different cloud environment -const SERVICE_SUFFIX_BLOB: &str = ".blob.core.windows.net"; -const SERVICE_SUFFIX_TABLE: &str = ".table.core.windows.net"; - pub trait Blob { fn list_blobs<'a>(&'a self) -> blob::requests::ListBlobBuilder<'a, No>; fn get_blob<'a>(&'a self) -> blob::requests::GetBlobBuilder<'a, No, No>; @@ -50,6 +46,8 @@ pub struct Client { account: String, key: String, hc: hyper::Client>, + blob_uri: String, + table_uri: String, } impl Blob for Client { @@ -178,11 +176,23 @@ impl Client { let client = hyper::Client::builder().build(hyper_tls::HttpsConnector::new(4)?); - Ok(Client { - account: account.to_owned(), - key: key.to_owned(), - hc: client, - }) + if cfg!(feature = "emulator") { + Ok(Client { + account: "devstoreaccount1".to_owned(), + key: "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==".to_owned(), + hc: client, + blob_uri: "http://127.0.0.1:10000/devstoreaccount1".to_owned(), + table_uri: "http://127.0.0.1:10002/devstoreaccount1".to_owned(), + }) + } else { + Ok(Client { + account: account.to_owned(), + key: key.to_owned(), + hc: client, + blob_uri: format!("https://{}.blob.core.windows.net", account), + table_uri: format!("https://{}.table.core.windows.net", account), + }) + } } pub fn account(&self) -> &str { @@ -193,6 +203,16 @@ impl Client { &self.key } + #[inline] + pub(crate) fn blob_uri(&self) -> &str { + &self.blob_uri + } + + #[inline] + pub(crate) fn table_uri(&self) -> &str { + &self.table_uri + } + pub(crate) fn perform_request( &self, uri: &str, @@ -230,12 +250,9 @@ impl Client { /// Uri scheme + authority e.g. http://myaccount.table.core.windows.net/ pub(crate) fn get_uri_prefix(&self, service_type: ServiceType) -> String { - "https://".to_owned() - + self.account() - + match service_type { - ServiceType::Blob => SERVICE_SUFFIX_BLOB, - ServiceType::Table => SERVICE_SUFFIX_TABLE, - } - + "/" + match service_type { + ServiceType::Blob => format!("{}/", self.blob_uri()), + ServiceType::Table => format!("{}/", self.table_uri()), + } } } diff --git a/src/azure/storage/container/create_request.rs b/src/azure/storage/container/create_request.rs deleted file mode 100644 index 80cf532d6b..0000000000 --- a/src/azure/storage/container/create_request.rs +++ /dev/null @@ -1,8 +0,0 @@ - - -pub struct CreateRequest { - name: String, - timeout: Option -} - -impl CreateRequest diff --git a/src/azure/storage/container/mod.rs b/src/azure/storage/container/mod.rs index 4499293a30..3f8d1d0405 100644 --- a/src/azure/storage/container/mod.rs +++ b/src/azure/storage/container/mod.rs @@ -229,14 +229,14 @@ where { match params { Some(ref params) => format!( - "https://{}.blob.core.windows.net/{}?{}", - t.client().account(), + "{}/{}?{}", + t.client().blob_uri(), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), params ), None => format!( - "https://{}.blob.core.windows.net/{}", - t.client().account(), + "{}/{}", + t.client().blob_uri(), utf8_percent_encode(t.container_name(), COMPLETE_ENCODE_SET), ), } diff --git a/src/azure/storage/container/requests/acquire_lease_builder.rs b/src/azure/storage/container/requests/acquire_lease_builder.rs index b13bced5fe..8c5e0b0f8e 100644 --- a/src/azure/storage/container/requests/acquire_lease_builder.rs +++ b/src/azure/storage/container/requests/acquire_lease_builder.rs @@ -250,13 +250,14 @@ impl<'a, ContainerNameSet, LeaseDurationSet> AcquireLeaseBuilder<'a, ContainerNa where ContainerNameSet: ToAssign, LeaseDurationSet: ToAssign, -{} +{ +} impl<'a> AcquireLeaseBuilder<'a, Yes, Yes> { pub fn finalize(self) -> impl Future { let mut uri = format!( - "https://{}.blob.core.windows.net/{}?comp=lease&restype=container", - self.client().account(), + "{}/{}?comp=lease&restype=container", + self.client().blob_uri(), self.container_name() ); diff --git a/src/azure/storage/container/requests/break_lease_builder.rs b/src/azure/storage/container/requests/break_lease_builder.rs index 373818985c..0092f9b49d 100644 --- a/src/azure/storage/container/requests/break_lease_builder.rs +++ b/src/azure/storage/container/requests/break_lease_builder.rs @@ -192,8 +192,8 @@ impl<'a, ContainerNameSet> BreakLeaseBuilder<'a, ContainerNameSet> where Contain impl<'a> BreakLeaseBuilder<'a, Yes> { pub fn finalize(self) -> impl Future { let mut uri = format!( - "https://{}.blob.core.windows.net/{}?comp=lease&restype=container", - self.client().account(), + "{}/{}?comp=lease&restype=container", + self.client().blob_uri(), self.container_name() ); diff --git a/src/azure/storage/container/requests/create_builder.rs b/src/azure/storage/container/requests/create_builder.rs index 3cfb88e151..44d0ddf946 100644 --- a/src/azure/storage/container/requests/create_builder.rs +++ b/src/azure/storage/container/requests/create_builder.rs @@ -165,7 +165,8 @@ impl<'a, ContainerNameSet, PublicAccessSet> CreateBuilder<'a, ContainerNameSet, where ContainerNameSet: ToAssign, PublicAccessSet: ToAssign, -{} +{ +} impl<'a, ContainerNameSet, PublicAccessSet> ContainerNameSupport<'a> for CreateBuilder<'a, ContainerNameSet, PublicAccessSet> where @@ -214,11 +215,8 @@ impl<'a> CreateBuilder<'a, No, No> { impl<'a> CreateBuilder<'a, Yes, Yes> { pub fn finalize(self) -> impl Future { - let mut uri = format!( - "https://{}.blob.core.windows.net/{}?restype=container", - self.client().account(), - self.container_name() - ); + let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name()); + println!("{}", uri); if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { uri = format!("{}&{}", uri, nm); diff --git a/src/azure/storage/container/requests/delete_builder.rs b/src/azure/storage/container/requests/delete_builder.rs index 983e9b7f01..5fd424f93c 100644 --- a/src/azure/storage/container/requests/delete_builder.rs +++ b/src/azure/storage/container/requests/delete_builder.rs @@ -146,11 +146,7 @@ impl<'a> DeleteBuilder<'a, No> { impl<'a> DeleteBuilder<'a, Yes> { pub fn finalize(self) -> impl Future { - let mut uri = format!( - "https://{}.blob.core.windows.net/{}?restype=container", - self.client().account(), - self.container_name() - ); + let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name()); if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { uri = format!("{}&{}", uri, nm); diff --git a/src/azure/storage/container/requests/get_acl_builder.rs b/src/azure/storage/container/requests/get_acl_builder.rs index b2c1b106a9..9bccf2bafb 100644 --- a/src/azure/storage/container/requests/get_acl_builder.rs +++ b/src/azure/storage/container/requests/get_acl_builder.rs @@ -72,11 +72,7 @@ where impl<'a> GetACLBuilder<'a, Yes> { pub fn finalize(self) -> impl Future { - let mut uri = format!( - "https://{}.blob.core.windows.net/{}?restype=container&comp=acl", - self.client().account(), - self.container_name() - ); + let mut uri = format!("{}/{}?restype=container&comp=acl", self.client().blob_uri(), self.container_name()); if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { uri = format!("{}&{}", uri, nm); diff --git a/src/azure/storage/container/requests/get_properties_builder.rs b/src/azure/storage/container/requests/get_properties_builder.rs index 40d7d8dca7..65c407ebbd 100644 --- a/src/azure/storage/container/requests/get_properties_builder.rs +++ b/src/azure/storage/container/requests/get_properties_builder.rs @@ -72,11 +72,7 @@ where impl<'a> GetPropertiesBuilder<'a, Yes> { pub fn finalize(self) -> impl Future { - let mut uri = format!( - "https://{}.blob.core.windows.net/{}?restype=container", - self.client().account(), - self.container_name() - ); + let mut uri = format!("{}/{}?restype=container", self.client().blob_uri(), self.container_name()); if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { uri = format!("{}&{}", uri, nm); diff --git a/src/azure/storage/container/requests/list_builder.rs b/src/azure/storage/container/requests/list_builder.rs index 79f14b8914..7c1dca0b56 100644 --- a/src/azure/storage/container/requests/list_builder.rs +++ b/src/azure/storage/container/requests/list_builder.rs @@ -78,11 +78,7 @@ impl<'a> ListBuilder<'a> { } pub fn finalize(self) -> impl Future { - let mut uri = format!( - "https://{}.blob.core.windows.net?comp=list&maxresults={}", - self.client().account(), - self.max_results() - ); + let mut uri = format!("{}?comp=list&maxresults={}", self.client().blob_uri(), self.max_results()); if self.is_metadata_included() { uri = format!("{}&include=metadata", uri); diff --git a/src/azure/storage/container/requests/release_lease_builder.rs b/src/azure/storage/container/requests/release_lease_builder.rs index f9838b2eae..3af8421df4 100644 --- a/src/azure/storage/container/requests/release_lease_builder.rs +++ b/src/azure/storage/container/requests/release_lease_builder.rs @@ -174,13 +174,14 @@ impl<'a, ContainerNameSet, LeaseIdSet> ReleaseLeaseBuilder<'a, ContainerNameSet, where ContainerNameSet: ToAssign, LeaseIdSet: ToAssign, -{} +{ +} impl<'a> ReleaseLeaseBuilder<'a, Yes, Yes> { pub fn finalize(self) -> impl Future { let mut uri = format!( - "https://{}.blob.core.windows.net/{}?comp=lease&restype=container", - self.client().account(), + "{}/{}?comp=lease&restype=container", + self.client().blob_uri(), self.container_name() ); diff --git a/src/azure/storage/container/requests/renew_lease_builder.rs b/src/azure/storage/container/requests/renew_lease_builder.rs index 7fc45b64e8..438e00206c 100644 --- a/src/azure/storage/container/requests/renew_lease_builder.rs +++ b/src/azure/storage/container/requests/renew_lease_builder.rs @@ -174,13 +174,14 @@ impl<'a, ContainerNameSet, LeaseIdSet> RenewLeaseBuilder<'a, ContainerNameSet, L where ContainerNameSet: ToAssign, LeaseIdSet: ToAssign, -{} +{ +} impl<'a> RenewLeaseBuilder<'a, Yes, Yes> { pub fn finalize(self) -> impl Future { let mut uri = format!( - "https://{}.blob.core.windows.net/{}?comp=lease&restype=container", - self.client().account(), + "{}/{}?comp=lease&restype=container", + self.client().blob_uri(), self.container_name() ); diff --git a/src/azure/storage/container/requests/set_acl_builder.rs b/src/azure/storage/container/requests/set_acl_builder.rs index a754346b2b..bdc72d3f12 100644 --- a/src/azure/storage/container/requests/set_acl_builder.rs +++ b/src/azure/storage/container/requests/set_acl_builder.rs @@ -112,11 +112,7 @@ where impl<'a> SetACLBuilder<'a, Yes, Yes> { pub fn finalize(self) -> impl Future { - let mut uri = format!( - "https://{}.blob.core.windows.net/{}?restype=container&comp=acl", - self.client().account(), - self.container_name() - ); + let mut uri = format!("{}/{}?restype=container&comp=acl", self.client().blob_uri(), self.container_name()); if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { uri = format!("{}&{}", uri, nm); diff --git a/src/azure/storage/rest_client.rs b/src/azure/storage/rest_client.rs index 84803c041a..16d2abf41a 100644 --- a/src/azure/storage/rest_client.rs +++ b/src/azure/storage/rest_client.rs @@ -67,7 +67,8 @@ fn string_to_sign(h: &HeaderMap, u: &url::Url, method: &Method, service_type: Se add_if_exists(h, header::CONTENT_TYPE), add_if_exists(h, HEADER_DATE), canonicalized_resource_table(u) - ).unwrap(); + ) + .unwrap(); s } _ => { @@ -95,7 +96,8 @@ fn string_to_sign(h: &HeaderMap, u: &url::Url, method: &Method, service_type: Se add_if_exists(h, header::RANGE), canonicalize_header(h), canonicalized_resource(u) - ).unwrap(); + ) + .unwrap(); s } } @@ -139,13 +141,17 @@ fn canonicalize_header(h: &HeaderMap) -> String { } #[inline] -fn get_account(u: &url::Url) -> String { +fn get_account(u: &url::Url) -> &str { match u.host().unwrap().clone() { url::Host::Domain(dm) => { // debug!("dom == {:?}", dm); let first_dot = dm.find('.').unwrap(); - String::from(&dm[0..first_dot]) + &dm[0..first_dot] + } + url::Host::Ipv4(_) => { + // this must be the emulator + "devstoreaccount1" } _ => panic!("only Domains are supported in canonicalized_resource"), } @@ -341,7 +347,8 @@ Wed, 03 May 2017 14:04:56 GMT let url = url::Url::parse( "http://myaccount.blob.core.windows.\ net/mycontainer?restype=container&comp=metadata", - ).unwrap(); + ) + .unwrap(); assert_eq!( super::canonicalized_resource(&url), "/myaccount/mycontainer\ncomp:metadata\nrestype:container" @@ -354,7 +361,8 @@ Wed, 03 May 2017 14:04:56 GMT "http://myaccount.blob.core.windows.\ net/mycontainer?restype=container&comp=list&include=snapshots&\ include=metadata&include=uncommittedblobs", - ).unwrap(); + ) + .unwrap(); assert_eq!( super::canonicalized_resource(&url), "/myaccount/mycontainer\ncomp:list\ninclude:metadata,snapshots,\ @@ -367,7 +375,8 @@ Wed, 03 May 2017 14:04:56 GMT let url = url::Url::parse( "https://myaccount-secondary.blob.core.windows.\ net/mycontainer/myblob", - ).unwrap(); + ) + .unwrap(); assert_eq!(super::canonicalized_resource(&url), "/myaccount-secondary/mycontainer/myblob"); }