diff --git a/src/v4/aliyun_util.rs b/src/v4/aliyun_util.rs index 4d12b7e..337fa91 100644 --- a/src/v4/aliyun_util.rs +++ b/src/v4/aliyun_util.rs @@ -18,10 +18,12 @@ struct Request { pub product: String, pub action: String, pub method: String, + pub endpoint: String, pub pathname: String, pub access_key: Option, pub user_agent: String, pub sign_algorithm: SignAlgorithm, + pub query: BTreeMap, pub headers: BTreeMap, pub stream: Option>, } @@ -29,7 +31,8 @@ struct Request { fn add_common_headers(header: &mut BTreeMap, request: &Request) { let (ymd, date) = get_timestamp(); - header.insert_kv(HEADER_HOST, "endpoint"); // TODO + let host = get_host(&request.endpoint); + header.insert_kv(HEADER_HOST, host); header.insert_kv(HEADER_X_ASC_VERSION, &request.version); header.insert_kv(HEADER_X_ASC_ACTION, &request.action); header.insert_kv(HEADER_USER_AGENT, &request.user_agent); @@ -37,16 +40,14 @@ fn add_common_headers(header: &mut BTreeMap, request: &Request) header.insert_kv(HEADER_X_ASC_SIGNATURE_NONCE, get_nonce()); header.insert_kv(HEADER_ACCEPT, CONTENT_TYPE_APPLICATION_JSON); - // TODO BODY ... - - // TODO signature ... + let digest_request_payload = digest_request_body(&request.sign_algorithm, &request.stream); match &request.sign_algorithm { SignAlgorithm::Sha1 => panic!("SHA1 in V4 is NOT supported!"), SignAlgorithm::Sha256 => { - header.insert_kv(HEADER_X_ASC_CONTENT_SHA256, ""); + header.insert_kv(HEADER_X_ASC_CONTENT_SHA256, &digest_request_payload); } SignAlgorithm::Sm3 => { - header.insert_kv(HEADER_X_ASC_CONTENT_SM3, ""); + header.insert_kv(HEADER_X_ASC_CONTENT_SM3, &digest_request_payload); } } @@ -92,6 +93,27 @@ fn get_timestamp() -> (String, String) { ("yyyymmdd".into(), "yyyy-mm-dd".into()) } +fn digest_request_body(sign_algorithm: &SignAlgorithm, body: &Option>) -> String { + match body { + None => sign_algorithm.digest(&vec![]), + Some(body) => sign_algorithm.digest(body), + } +} + +fn get_host(endpoint: &str) -> String { + let lower_endpoint = endpoint.to_ascii_lowercase(); + let skip_chars = if lower_endpoint.starts_with("http://") { + 7 + } else if lower_endpoint.starts_with("https://") { + 8 + } else if lower_endpoint.starts_with("//") { + 2 + } else { + 0 + }; + lower_endpoint.chars().skip(skip_chars).take_while(|c| *c != '/').collect() +} + fn get_nonce() -> String { let seq = SEQ.fetch_add(1, Ordering::Relaxed); let now = SystemTime::now(); @@ -227,6 +249,31 @@ fn test_get_nonce() { assert!(nonce.starts_with("nonce-")); } +#[test] +fn test_digest_request_body() { + assert_eq!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + digest_request_body(&SignAlgorithm::Sha256, &None).as_str()); + assert_eq!("ae6d8a1c07f438667618879eae74ffab84c236ea5d8ca52d5ca0523cc35e8bb9", + digest_request_body(&SignAlgorithm::Sha256, &Some("Test Body".as_bytes().to_vec())).as_str()); + assert_eq!("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", + digest_request_body(&SignAlgorithm::Sm3, &None).as_str()); + assert_eq!("487b3b171a56ba378f0bfd6d6e430774728aa23c13121eba18c54cf39feac0da", + digest_request_body(&SignAlgorithm::Sm3, &Some("Test Body".as_bytes().to_vec())).as_str()); +} + +#[test] +fn test_get_host() { + assert_eq!("example.com", get_host("http://example.com").as_str()); + assert_eq!("example.com", get_host("http://example.com/").as_str()); + assert_eq!("example.com", get_host("http://example.com/path").as_str()); + assert_eq!("example.com", get_host("https://example.com/path").as_str()); + assert_eq!("example.com", get_host("HttPS://EXAMPLE.com/path").as_str()); + assert_eq!("example.com", get_host("//EXAMPLE.com/path").as_str()); + assert_eq!("example.com", get_host("EXAMPLE.com/path").as_str()); + assert_eq!("example.com:8000", get_host("EXAMPLE.com:8000/path").as_str()); + assert_eq!("example.com:8000", get_host("hTTPS://EXAMPLE.com:8000/path").as_str()); +} + #[test] fn test_get_signing_key_sha256() { let signing_key = get_signing_key(