Skip to content

Commit 4539963

Browse files
committed
Merge branch 'ergonomic-futures'
2 parents c794980 + fb37a88 commit 4539963

File tree

6 files changed

+51
-50
lines changed

6 files changed

+51
-50
lines changed

Cargo.toml

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "rust-s3"
3-
version = "0.18.0-beta4"
3+
version = "0.18.0"
44
authors = ["Drazen Urch", "Nick Stevens"]
5-
description = "Tiny Rust library for working with Amazon S3."
5+
description = "Tiny Rust library for working with Amazon S3 and compatible APIs"
66
repository = "https://github.com/durch/rust-s3"
77
readme = "README.md"
88
keywords = ["Amazon", "AWS", "S3"]
@@ -16,17 +16,17 @@ path = "src/lib.rs"
1616
[dependencies]
1717
base64 = "0.10.1"
1818
chrono = "^0.4.0"
19-
hex = "^0.2.0"
20-
hmac = "^0.4.2"
19+
hex = "^0.4.0"
20+
hmac = "^0.7.1"
2121
reqwest = "^0.9.1"
22-
serde_derive = "^1.0.11"
23-
serde = "^1.0.11"
24-
serde-xml-rs = "^0.2.1"
25-
sha2 = "^0.6.0"
22+
serde_derive = "^1.0.101"
23+
serde = "^1.0.101"
24+
serde-xml-rs = "^0.3.1"
25+
sha2 = "^0.8.0"
2626
md5 = "^0.6.1"
27-
url = "^1.5.1"
28-
rust-ini = "^0.9"
29-
dirs = "^1.0"
27+
url = "^2.1.0"
28+
rust-ini = "^0.13"
29+
dirs = "^2.0.2"
3030
futures = "^0.1.28"
3131

3232
[features]

README.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@
66
[![Join the chat at https://gitter.im/durch/rust-s3](https://badges.gitter.im/durch/rust-s3.svg)](https://gitter.im/durch/rust-s3?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
77
## rust-s3 [[docs](https://durch.github.io/rust-s3/)]
88

9-
Tiny Rust library for working with Amazon S3 or arbitrary S3 compatible APIs
9+
Tiny Rust library for working with Amazon S3 or arbitrary S3 compatible APIs, fully compatible with *async* usage
1010

1111
### Intro
12-
Very modest interface towards Amazon S3.
13-
Supports `put`, `get`, `list`, and `delete`.
12+
Very modest interface towards Amazon S3, as well as S3 compatible APIs.
13+
Supports `put`, `get`, `list`, and `delete`, operations on `tags` and `location`.
14+
15+
Supports streaming S3 contents generic over `T: Write` as of `0.15.0`.
1416

1517
### What is cool
1618

1719
The main cool feature is that `put` commands return a presigned link to the file you uploaded.
1820
This means you can upload to s3, and give the link to select people without having to worry about publicly accessible files on S3.
1921

20-
Also supports streaming S3 contents generic over `T: Write` as of `0.15.0`.
2122

2223
### Configuration
2324

@@ -30,13 +31,13 @@ it is configured for one week which is the maximum Amazon allows ATM.
3031

3132
```
3233
[dependencies]
33-
rust-s3 = "0.15.0"
34+
rust-s3 = "0.18.0"
3435
```
3536

3637
#### Disable SSL verification for endpoints
3738
```
3839
[dependencies]
39-
rust-s3 = {version = "0.15.0", features = ["no-verify-ssl"]}
40+
rust-s3 = {version = "0.18.0", features = ["no-verify-ssl"]}
4041
```
4142

4243
#### Example

src/bucket.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ impl Bucket {
193193
let request = Request::new(self, "?location", Command::GetBucketLocation);
194194
let result = request.response_data()?;
195195
let region_string = String::from_utf8_lossy(&result.0);
196-
let region = match serde_xml::deserialize(region_string.as_bytes()) {
196+
let region = match serde_xml::from_reader(region_string.as_bytes()) {
197197
Ok(r) => {
198198
let location_result: BucketLocationResult = r;
199199
location_result.region.parse()?
@@ -364,7 +364,7 @@ impl Bucket {
364364
let tagging = if result.1 == 200 {
365365
let result_string = String::from_utf8_lossy(&result.0);
366366
println!("{}", result_string);
367-
Some(serde_xml::deserialize(result_string.as_bytes())?)
367+
Some(serde_xml::from_reader(result_string.as_bytes())?)
368368
} else {
369369
None
370370
};
@@ -385,7 +385,7 @@ impl Bucket {
385385
let request = Request::new(self, "/", command);
386386
let result = request.response_data()?;
387387
let result_string = String::from_utf8_lossy(&result.0);
388-
let deserialized: ListBucketResult = serde_xml::deserialize(result_string.as_bytes())?;
388+
let deserialized: ListBucketResult = serde_xml::from_reader(result_string.as_bytes())?;
389389
Ok((deserialized, result.1))
390390
}
391391

@@ -402,7 +402,7 @@ impl Bucket {
402402
let request = Request::new(self, "/", command);
403403
let result = request.response_data_future();
404404
result.and_then(|(response, status_code)|
405-
match serde_xml::deserialize(response.as_slice()) {
405+
match serde_xml::from_reader(response.as_slice()) {
406406
Ok(list_bucket_result) => Ok((list_bucket_result, status_code)),
407407
Err(e) => {
408408
panic!("Could not deserialize list bucket result: {}", e);

src/error.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ impl_from!(reqwest::header::InvalidHeaderName);
2020
impl_from!(reqwest::header::InvalidHeaderValue);
2121
impl_from!(std::env::VarError);
2222
impl_from!(ini::ini::Error);
23+
impl_from!(hmac::crypto_mac::InvalidKeyLength);
2324

2425
#[derive(Debug)]
2526
pub struct S3Error {

src/request.rs

+13-15
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ use std::io::{Read, Write};
77
use bucket::Bucket;
88
use chrono::{DateTime, Utc};
99
use command::Command;
10-
use hmac::{Hmac, Mac};
10+
use hmac::Mac;
1111
use reqwest::async as async;
1212
use reqwest::header::{self, HeaderMap, HeaderName, HeaderValue};
1313
use sha2::{Digest, Sha256};
14-
use hex::ToHex;
1514
use url::Url;
1615

1716
use futures::prelude::*;
@@ -121,12 +120,12 @@ impl<'a> Request<'a> {
121120
Command::PutObject { content, .. } => {
122121
let mut sha = Sha256::default();
123122
sha.input(content);
124-
sha.result().as_slice().to_hex()
123+
hex::encode(sha.result().as_slice())
125124
}
126125
Command::PutObjectTagging { tags } => {
127126
let mut sha = Sha256::default();
128127
sha.input(tags.as_bytes());
129-
sha.result().as_slice().to_hex()
128+
hex::encode(sha.result().as_slice())
130129
}
131130
_ => EMPTY_PAYLOAD_SHA.into(),
132131
}
@@ -149,29 +148,29 @@ impl<'a> Request<'a> {
149148
signing::string_to_sign(&self.datetime, &self.bucket.region(), request)
150149
}
151150

152-
fn signing_key(&self) -> Vec<u8> {
153-
signing::signing_key(
151+
fn signing_key(&self) -> S3Result<Vec<u8>> {
152+
Ok(signing::signing_key(
154153
&self.datetime,
155154
&self.bucket.secret_key(),
156155
&self.bucket.region(),
157156
"s3",
158-
)
157+
)?)
159158
}
160159

161-
fn authorization(&self, headers: &HeaderMap) -> String {
160+
fn authorization(&self, headers: &HeaderMap) -> S3Result<String> {
162161
let canonical_request = self.canonical_request(headers);
163162
let string_to_sign = self.string_to_sign(&canonical_request);
164-
let mut hmac = Hmac::<Sha256>::new(&self.signing_key());
163+
let mut hmac = signing::HmacSha256::new_varkey(&self.signing_key()?)?;
165164
hmac.input(string_to_sign.as_bytes());
166-
let signature = hmac.result().code().to_hex();
165+
let signature = hex::encode(hmac.result().code());
167166
let signed_header = signing::signed_header_string(headers);
168-
signing::authorization_header(
167+
Ok(signing::authorization_header(
169168
&self.bucket.access_key(),
170169
&self.datetime,
171170
&self.bucket.region(),
172171
&signed_header,
173172
&signature,
174-
)
173+
))
175174
}
176175

177176
fn headers(&self) -> S3Result<HeaderMap> {
@@ -209,7 +208,7 @@ impl<'a> Request<'a> {
209208
}
210209

211210
// This must be last, as it signs the other headers
212-
let authorization = self.authorization(&headers);
211+
let authorization = self.authorization(&headers)?;
213212
headers.insert(header::AUTHORIZATION, authorization.parse()?);
214213

215214
// The format of RFC2822 is somewhat malleable, so including it in
@@ -260,7 +259,7 @@ impl<'a> Request<'a> {
260259
};
261260

262261
let request = client
263-
.request(self.command.http_verb(), self.url())
262+
.request(self.command.http_verb(), self.url().as_str())
264263
.headers(headers.to_owned())
265264
.body(content.to_owned());
266265

@@ -291,7 +290,6 @@ mod tests {
291290
use command::Command;
292291
use credentials::Credentials;
293292
use request::{Request};
294-
use url::form_urlencoded::Parse;
295293
use error::S3Result;
296294

297295
// Fake keys - otherwise using Credentials::default will use actual user

src/signing.rs

+15-14
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
use std::str;
66

77
use chrono::{DateTime, Utc};
8-
use hex::ToHex;
98
use hmac::{Hmac, Mac};
109
use url::Url;
1110
use region::Region;
1211
use reqwest::header::HeaderMap;
1312
use sha2::{Digest, Sha256};
13+
use error::S3Result;
1414

1515
const SHORT_DATE: &str = "%Y%m%d";
1616
const LONG_DATETIME: &str = "%Y%m%dT%H%M%SZ";
1717

18+
pub type HmacSha256 = Hmac<Sha256>;
19+
1820
/// Encode a URI following the specific requirements of the AWS service.
1921
pub fn uri_encode(string: &str, encode_slash: bool) -> String {
2022
let mut result = String::with_capacity(string.len() * 2);
@@ -102,7 +104,7 @@ pub fn string_to_sign(datetime: &DateTime<Utc>, region: &Region, canonical_req:
102104
format!("AWS4-HMAC-SHA256\n{timestamp}\n{scope}\n{hash}",
103105
timestamp = datetime.format(LONG_DATETIME),
104106
scope = scope_string(datetime, region),
105-
hash = hasher.result().as_slice().to_hex())
107+
hash = hex::encode(hasher.result().as_slice()))
106108
}
107109

108110
/// Generate the AWS signing key, derived from the secret key, date, region,
@@ -111,17 +113,17 @@ pub fn signing_key(datetime: &DateTime<Utc>,
111113
secret_key: &str,
112114
region: &Region,
113115
service: &str)
114-
-> Vec<u8> {
116+
-> S3Result<Vec<u8>> {
115117
let secret = String::from("AWS4") + secret_key;
116-
let mut date_hmac = Hmac::<Sha256>::new(secret.as_bytes());
118+
let mut date_hmac = HmacSha256::new_varkey(secret.as_bytes())?;
117119
date_hmac.input(datetime.format(SHORT_DATE).to_string().as_bytes());
118-
let mut region_hmac = Hmac::<Sha256>::new(date_hmac.result().code());
120+
let mut region_hmac = HmacSha256::new_varkey(&date_hmac.result().code())?;
119121
region_hmac.input(region.to_string().as_bytes());
120-
let mut service_hmac = Hmac::<Sha256>::new(region_hmac.result().code());
122+
let mut service_hmac = HmacSha256::new_varkey(&region_hmac.result().code())?;
121123
service_hmac.input(service.as_bytes());
122-
let mut signing_hmac = Hmac::<Sha256>::new(service_hmac.result().code());
124+
let mut signing_hmac = HmacSha256::new_varkey(&service_hmac.result().code())?;
123125
signing_hmac.input(b"aws4_request");
124-
signing_hmac.result().code().into()
126+
Ok(signing_hmac.result().code().to_vec())
125127
}
126128

127129
/// Generate the AWS authorization header.
@@ -144,7 +146,6 @@ mod tests {
144146
use std::str;
145147

146148
use chrono::{TimeZone, Utc};
147-
use hex::ToHex;
148149
use reqwest::header::{HeaderMap, HeaderValue};
149150
use url::Url;
150151

@@ -200,8 +201,8 @@ mod tests {
200201
let key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
201202
let expected = "c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9";
202203
let datetime = Utc.ymd(2015, 8, 30).and_hms(0, 0, 0);
203-
let signature = signing_key(&datetime, key, &"us-east-1".parse().unwrap(), "iam");
204-
assert_eq!(expected, signature.to_hex());
204+
let signature = signing_key(&datetime, key, &"us-east-1".parse().unwrap(), "iam").unwrap();
205+
assert_eq!(expected, hex::encode(signature));
205206
}
206207

207208
const EXPECTED_SHA: &'static str = "e3b0c44298fc1c149afbf4c8996fb924\
@@ -251,9 +252,9 @@ mod tests {
251252
let expected = "f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41";
252253
let secret = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
253254
let signing_key = signing_key(&datetime, secret, &"us-east-1".parse().unwrap(), "s3");
254-
let mut hmac = Hmac::<Sha256>::new(&signing_key);
255+
let mut hmac = Hmac::<Sha256>::new_varkey(&signing_key.unwrap()).unwrap();
255256
hmac.input(string_to_sign.as_bytes());
256-
assert_eq!(expected, hmac.result().code().to_hex());
257+
assert_eq!(expected, hex::encode(hmac.result().code()));
257258
}
258259

259260
#[test]
@@ -269,7 +270,7 @@ mod tests {
269270
<IsTruncated>true</IsTruncated>
270271
</ListBucketResult>
271272
"###;
272-
let deserialized: ListBucketResult = serde_xml::deserialize(result_string.as_bytes()).expect("Parse error!");
273+
let deserialized: ListBucketResult = serde_xml::from_reader(result_string.as_bytes()).expect("Parse error!");
273274
assert!(deserialized.is_truncated);
274275
}
275276
}

0 commit comments

Comments
 (0)