1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::env;
use std::time::SystemTime;

use aws_auth::{self, AwsRegion, AwsService, FormatTime, Hash, HttpMethod};

const TODO_REGION: AwsRegion = AwsRegion::ApNortheast1;

// TODO: error handling
fn bucket_key_from_s3_url(mut s3_url: &str) -> (&str, &str) {
    if s3_url.starts_with("s3://") {
        s3_url = &s3_url["s3://".len()..];
    } else {
        panic!()
    }
    let mut split = s3_url.splitn(2, '/');
    let bucket = split.next().expect("no domain");
    let key = split.next().expect("no key");
    (bucket, key)
}

fn s3_url_to_http_url(s3_url: &str, region: AwsRegion) -> String {
    let (bucket, key) = bucket_key_from_s3_url(s3_url);
    let path_style = bucket.contains('.');

    match path_style {
        true => format!(
            "https://s3.{}.amazonaws.com/{}/{}",
            region.to_str(),
            bucket,
            key
        ),
        false => format!(
            "https://{}.s3.{}.amazonaws.com/{}",
            bucket,
            region.to_str(),
            key
        ),
    }
}

// TODO: query and headers
fn easy_auth_header_now(
    http_method: HttpMethod,
    https_url: &str,
    secret_key: &str,
    key_id: &str,
    region: AwsRegion,
    payload: &[u8],
) -> (Vec<u8>, Hash, SystemTime) {
    let mut buffer = Vec::new();
    let secret_key = aws_auth::validate_secret_key(&secret_key).expect("invalid secret key");
    let key_id = aws_auth::validate_key_id(&key_id).expect("invalid key id");

    let (domain, abspath, query) = aws_auth::split_url(https_url.as_bytes());
    let mut query = query.collect::<Vec<_>>();
    aws_auth::ensure_query_order(&mut query);

    let request_time = SystemTime::now();
    let payload_hash = Hash::new(payload);
    let mut request_time_header = [0; 16];
    request_time.write_iso8602_basic_seconds_utc(&mut request_time_header);
    let signed_headers = vec![
        (&b"host"[..], domain),
        (&b"x-amz-content-sha256"[..], &payload_hash.as_hex()[..]),
        (&b"x-amz-date"[..], &request_time_header[..]),
    ];
    let key_date = SystemTime::now();
    let signing_key = aws_auth::signing_key(&secret_key, key_date, region, AwsService::S3);

    aws_auth::gen_auth_header(
        &mut buffer,
        http_method,
        abspath,
        &query,
        &signed_headers,
        &signing_key,
        key_id,
        region,
        AwsService::S3,
        request_time,
        key_date,
        &payload_hash,
    );

    (buffer, payload_hash, key_date)
}

pub async fn quick_get(s3_url: &str) -> Result<reqwest::Response, reqwest::Error> {
    let https_url = s3_url_to_http_url(s3_url, TODO_REGION);
    let secret_key = env::var("AWS_SECRET_ACCESS_KEY").expect("no secretkey");
    let key_id = env::var("AWS_ACCESS_KEY_ID").expect("no key id");
    let region =
        AwsRegion::try_from(&env::var("AWS_REGION").expect("no region")).expect("invalid region");
    let (auth_header, payload_hash, key_date) = easy_auth_header_now(
        HttpMethod::Get,
        &https_url,
        &secret_key,
        &key_id,
        region,
        "".as_bytes(),
    );

    let mut date_header = [0; 16];
    key_date.write_iso8602_basic_seconds_utc(&mut date_header);

    reqwest::ClientBuilder::new()
        .build()
        .unwrap()
        .get(&https_url)
        .header("Authorization", auth_header.as_slice())
        .header("x-amz-content-sha256", &payload_hash.as_hex()[..])
        .header("x-amz-date", &date_header[..])
        .send()
        .await
}