Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate SDKs from http::Request to azure_core::Request #833

Merged
merged 35 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9c833dc
remove http_client from pipeline
ctaggart Jun 18, 2022
8159e7f
remove execute_request_check_status from HttpClient
ctaggart Jun 18, 2022
e58ddb4
reexport Url
ctaggart Jun 18, 2022
9d8ab08
add Request headers functions
ctaggart Jun 18, 2022
695d9a7
add execute_request_check_status to ServiceClient
ctaggart Jun 18, 2022
3c0dcbe
iot_hub builders & responses
ctaggart Jun 18, 2022
19512e4
add execute_request_check_status to core
ctaggart Jun 18, 2022
e9eb0f7
add CollectedResponse to core
ctaggart Jun 18, 2022
5284a05
remove temp hack
ctaggart Jun 18, 2022
3a65374
update iot_hub
ctaggart Jun 18, 2022
174009c
update messaging_servicebus
ctaggart Jun 18, 2022
5e5f587
data_tables checks
ctaggart Jun 18, 2022
9c8733d
migrate http::HeaderMap to azure_core::headers::Headers
ctaggart Jun 18, 2022
f9ea684
data_tables checks
ctaggart Jun 18, 2022
e889c53
data_cosmos checks
ctaggart Jun 18, 2022
766bbf1
messaging_servicebus checks
ctaggart Jun 18, 2022
6ae1eb8
storage_blobs checks
ctaggart Jun 19, 2022
d98e816
storage_datalake checks
ctaggart Jun 19, 2022
ebac3ff
storage_queues checks
ctaggart Jun 19, 2022
c84836b
sdk checks
ctaggart Jun 19, 2022
5a440ab
--no-default-features checks
ctaggart Jun 19, 2022
20e9a6d
wasm checks
ctaggart Jun 19, 2022
772abbd
mock_transport_framework checks
ctaggart Jun 19, 2022
c6a19e2
modify services codegen
ctaggart Jun 19, 2022
ae3b69c
gen services
ctaggart Jun 19, 2022
fbe44ac
Merge remote-tracking branch 'origin/main' into http_client
ctaggart Jun 20, 2022
c16b15d
remove extra comma
cataggar Jun 20, 2022
3785f84
rename from execute_request2 to execute_request
ctaggart Jun 20, 2022
4e57086
move execute_request_check_status back to trait function
ctaggart Jun 20, 2022
8ac1a64
get http_client() like before
ctaggart Jun 20, 2022
1b89779
Merge branch 'http_client' of ctaggart:ctaggart/azure-sdk-for-rust in…
ctaggart Jun 20, 2022
5dc407c
restore status code range
ctaggart Jun 20, 2022
54d9430
add deprecated message back
ctaggart Jun 20, 2022
11d53ae
changes from PR review
ctaggart Jun 21, 2022
1deb9cf
Merge remote-tracking branch 'origin/main' into http_client
ctaggart Jun 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions sdk/core/src/error/http_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ impl HttpError {
let mut error_code = get_error_code_from_header(&response);
let mut headers = HashMap::new();

for (name, value) in response.headers() {
let value = String::from_utf8_lossy(value.as_bytes()).to_string();
headers.insert(name.to_string(), value);
for (name, value) in response.headers().iter() {
headers.insert(name.as_str().to_string(), value.as_str().to_string());
}

let body = response.into_body().await;
Expand Down Expand Up @@ -76,9 +75,8 @@ fn get_error_code_from_header(response: &Response) -> Option<String> {
Some(
response
.headers()
.get(http::header::HeaderName::from_static("x-ms-error-code"))?
.to_str()
.ok()?
.get("x-ms-error-code")?
.as_str()
cataggar marked this conversation as resolved.
Show resolved Hide resolved
.to_owned(),
)
}
Expand Down
112 changes: 82 additions & 30 deletions sdk/core/src/headers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
//! Azure HTTP headers.
mod utilities;

use crate::error::{Error, ErrorKind, ResultExt};
use std::{collections::HashMap, fmt::Debug, str::FromStr};
pub use utilities::*;

use http::request::Builder;
use std::collections::HashMap;

/// A trait for converting a type into request headers
pub trait AsHeaders {
type Iter: Iterator<Item = (HeaderName, HeaderValue)>;
Expand Down Expand Up @@ -52,7 +51,7 @@ pub trait Header {
}

/// A collection of headers
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Headers(std::collections::HashMap<HeaderName, HeaderValue>);

impl Headers {
Expand All @@ -65,6 +64,80 @@ impl Headers {
self.0.get(&key.into())
}

/// Get a header value or error if it is not found
pub fn get_or_err<T: Into<HeaderName>>(&self, key: T) -> crate::Result<&HeaderValue> {
let key: &HeaderName = &key.into();
let value = self.0.get(key);
value.ok_or_else(|| {
Error::with_message(ErrorKind::Other, || {
format!("header not found {}", key.as_str())
})
})
}

/// Get a header value as a str
pub fn get_as_str<T: Into<HeaderName>>(&self, key: T) -> Option<&str> {
self.get(key).map(|v| v.as_str())
}
cataggar marked this conversation as resolved.
Show resolved Hide resolved

/// Get a header value as a str or error if it is not found
pub fn get_as_str_or_err<T: Into<HeaderName>>(&self, key: T) -> crate::Result<&str> {
self.get_or_err(key).map(|v| v.as_str())
}

/// Get a header value as a String
pub fn get_as_string<T: Into<HeaderName>>(&self, key: T) -> Option<String> {
self.get(key).map(|v| v.as_str().to_string())
}

/// Get a header value as a String or error if it is not found
pub fn get_as_string_or_err<T: Into<HeaderName>>(&self, key: T) -> crate::Result<String> {
self.get_or_err(key).map(|v| v.as_str().to_string())
}

/// Get a header value as a u64
pub fn get_as_u64<T: Into<HeaderName>>(&self, key: T) -> crate::Result<Option<u64>> {
let key = key.into();
self.get(key.clone())
.map(|v: &HeaderValue| {
let v = v.as_str();
v.parse::<u64>()
.with_context(ErrorKind::DataConversion, || {
format!("unable to parse header into u64 {key:?}: {v}")
})
})
.transpose()
}

/// Get a header value as a u64 or error if it is not found
pub fn get_as_u64_or_err<T: Into<HeaderName>>(&self, key: T) -> crate::Result<u64> {
let key = key.into();
let v = self.get_or_err(key.clone())?;
let v = v.as_str();
v.parse::<u64>()
.with_context(ErrorKind::DataConversion, || {
format!("unable to parse header into u64 {key:?}: {v}")
})
}

pub fn get_as_enum<T: Into<HeaderName>, V: FromStr<Err = E>, E>(
cataggar marked this conversation as resolved.
Show resolved Hide resolved
&self,
key: T,
) -> crate::Result<Option<V>>
where
E: std::error::Error + Send + Sync + 'static,
{
let key = key.into();
self.get(key.clone())
.map(|v: &HeaderValue| {
let v = v.as_str();
v.parse::<V>().with_context(ErrorKind::DataConversion, || {
format!("unable to parse header into enum {key:?}: {v}")
})
})
.transpose()
}

/// Insert a header name/value pair
pub fn insert<K, V>(&mut self, key: K, value: V)
where
Expand Down Expand Up @@ -96,16 +169,16 @@ impl From<std::collections::HashMap<HeaderName, HeaderValue>> for Headers {
}
}

impl From<http::HeaderMap> for Headers {
fn from(map: http::HeaderMap) -> Self {
impl From<&http::HeaderMap> for Headers {
fn from(map: &http::HeaderMap) -> Self {
let map = map
.into_iter()
.filter_map(|(k, v)| {
let key = k?.as_str().to_owned();
.map(|(k, v)| {
let key = k.as_str().to_owned();
let value = std::str::from_utf8(v.as_bytes())
.expect("non-UTF8 header value")
.to_owned();
Some((key.into(), value.into()))
(key.into(), value.into())
})
.collect::<HashMap<HeaderName, HeaderValue>>();
Self(map)
Expand Down Expand Up @@ -188,27 +261,6 @@ impl From<&HeaderValue> for http::header::HeaderValue {
}
}

#[must_use]
pub fn add_optional_header_ref<T: Header>(item: &Option<&T>, mut builder: Builder) -> Builder {
if let Some(item) = item {
builder = builder.header(item.name().as_str(), item.value().as_str())
}
builder
}

#[must_use]
pub fn add_optional_header<T: Header>(item: &Option<T>, mut builder: Builder) -> Builder {
if let Some(item) = item {
builder = builder.header(item.name().as_str(), item.value().as_str())
}
builder
}

#[must_use]
pub fn add_mandatory_header<T: Header>(item: &T, builder: Builder) -> Builder {
builder.header(item.name().as_str(), item.value().as_str())
}

// headers are case insensitive
// we are using all lowercase values
// same as https://github.com/hyperium/http/blob/master/util/src/main.rs
Expand Down
63 changes: 28 additions & 35 deletions sdk/core/src/headers/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,28 @@ use crate::{RequestId, SessionToken};

use chrono::{DateTime, FixedOffset, Utc};
use http::header::{DATE, ETAG, LAST_MODIFIED, SERVER};
use http::HeaderMap;
use std::str::FromStr;

pub fn get_option_str_from_headers<'a>(
headers: &'a HeaderMap,
headers: &'a Headers,
key: &str,
) -> crate::Result<Option<&'a str>> {
let h = match headers.get(key) {
let h = match headers.get(key.to_owned()) {
Some(h) => h,
None => return Ok(None),
};
Ok(Some(h.to_str().map_err(|e| {
Error::full(
ErrorKind::DataConversion,
e,
format!("could not convert header '{key}' to string"),
)
})?))
Ok(Some(h.as_str()))
}

pub fn get_str_from_headers<'a>(headers: &'a HeaderMap, key: &str) -> crate::Result<&'a str> {
pub fn get_str_from_headers<'a>(headers: &'a Headers, key: &str) -> crate::Result<&'a str> {
get_option_str_from_headers(headers, key)?.ok_or_else(|| {
Error::with_message(ErrorKind::DataConversion, || {
format!("could not find '{key}' in headers")
})
})
}

pub fn get_option_from_headers<T>(headers: &HeaderMap, key: &str) -> crate::Result<Option<T>>
pub fn get_option_from_headers<T>(headers: &Headers, key: &str) -> crate::Result<Option<T>>
where
T: std::str::FromStr + 'static,
T::Err: std::error::Error + Send + Sync,
Expand All @@ -56,7 +49,7 @@ where
})?))
}

pub fn get_from_headers<T>(headers: &HeaderMap, key: &str) -> crate::Result<T>
pub fn get_from_headers<T>(headers: &Headers, key: &str) -> crate::Result<T>
where
T: std::str::FromStr + 'static,
T::Err: std::error::Error + Send + Sync,
Expand Down Expand Up @@ -110,36 +103,36 @@ where
})
}

pub fn lease_id_from_headers(headers: &HeaderMap) -> crate::Result<LeaseId> {
pub fn lease_id_from_headers(headers: &Headers) -> crate::Result<LeaseId> {
get_from_headers(headers, LEASE_ID)
}

pub fn request_id_from_headers(headers: &HeaderMap) -> crate::Result<RequestId> {
pub fn request_id_from_headers(headers: &Headers) -> crate::Result<RequestId> {
get_from_headers(headers, REQUEST_ID)
}

pub fn client_request_id_from_headers_optional(headers: &HeaderMap) -> Option<String> {
pub fn client_request_id_from_headers_optional(headers: &Headers) -> Option<String> {
get_option_from_headers(headers, CLIENT_REQUEST_ID)
.ok()
.flatten()
}

pub fn last_modified_from_headers_optional(
headers: &HeaderMap,
headers: &Headers,
) -> crate::Result<Option<DateTime<Utc>>> {
get_option_from_headers(headers, LAST_MODIFIED.as_str())
}

pub fn date_from_headers(headers: &HeaderMap) -> crate::Result<DateTime<Utc>> {
pub fn date_from_headers(headers: &Headers) -> crate::Result<DateTime<Utc>> {
rfc2822_from_headers_mandatory(headers, DATE.as_str())
}

pub fn last_modified_from_headers(headers: &HeaderMap) -> crate::Result<DateTime<Utc>> {
pub fn last_modified_from_headers(headers: &Headers) -> crate::Result<DateTime<Utc>> {
rfc2822_from_headers_mandatory(headers, LAST_MODIFIED.as_str())
}

pub fn rfc2822_from_headers_mandatory(
headers: &HeaderMap,
headers: &Headers,
header_name: &str,
) -> crate::Result<DateTime<Utc>> {
let date = get_str_from_headers(headers, header_name)?;
Expand All @@ -152,65 +145,65 @@ pub fn utc_date_from_rfc2822(date: &str) -> crate::Result<DateTime<Utc>> {
}

pub fn continuation_token_from_headers_optional(
headers: &HeaderMap,
headers: &Headers,
) -> crate::Result<Option<String>> {
Ok(get_option_str_from_headers(headers, CONTINUATION)?.map(String::from))
}

pub fn sku_name_from_headers(headers: &HeaderMap) -> crate::Result<String> {
pub fn sku_name_from_headers(headers: &Headers) -> crate::Result<String> {
Ok(get_str_from_headers(headers, SKU_NAME)?.to_owned())
}

pub fn account_kind_from_headers(headers: &HeaderMap) -> crate::Result<String> {
pub fn account_kind_from_headers(headers: &Headers) -> crate::Result<String> {
Ok(get_str_from_headers(headers, ACCOUNT_KIND)?.to_owned())
}

pub fn etag_from_headers_optional(headers: &HeaderMap) -> crate::Result<Option<String>> {
pub fn etag_from_headers_optional(headers: &Headers) -> crate::Result<Option<String>> {
Ok(get_option_str_from_headers(headers, ETAG.as_str())?.map(String::from))
}

pub fn etag_from_headers(headers: &HeaderMap) -> crate::Result<String> {
pub fn etag_from_headers(headers: &Headers) -> crate::Result<String> {
Ok(get_str_from_headers(headers, ETAG.as_str())?.to_owned())
}

pub fn lease_time_from_headers(headers: &HeaderMap) -> crate::Result<u8> {
pub fn lease_time_from_headers(headers: &Headers) -> crate::Result<u8> {
get_from_headers(headers, LEASE_TIME)
}

#[cfg(not(feature = "azurite_workaround"))]
pub fn delete_type_permanent_from_headers(headers: &HeaderMap) -> crate::Result<bool> {
pub fn delete_type_permanent_from_headers(headers: &Headers) -> crate::Result<bool> {
get_from_headers(headers, DELETE_TYPE_PERMANENT)
}

#[cfg(feature = "azurite_workaround")]
pub fn delete_type_permanent_from_headers(headers: &HeaderMap) -> crate::Result<Option<bool>> {
pub fn delete_type_permanent_from_headers(headers: &Headers) -> crate::Result<Option<bool>> {
get_option_from_headers(headers, DELETE_TYPE_PERMANENT)
}

pub fn sequence_number_from_headers(headers: &HeaderMap) -> crate::Result<u64> {
pub fn sequence_number_from_headers(headers: &Headers) -> crate::Result<u64> {
get_from_headers(headers, BLOB_SEQUENCE_NUMBER)
}

pub fn session_token_from_headers(headers: &HeaderMap) -> crate::Result<SessionToken> {
pub fn session_token_from_headers(headers: &Headers) -> crate::Result<SessionToken> {
get_str_from_headers(headers, SESSION_TOKEN).map(ToOwned::to_owned)
}

pub fn server_from_headers(headers: &HeaderMap) -> crate::Result<&str> {
pub fn server_from_headers(headers: &Headers) -> crate::Result<&str> {
get_str_from_headers(headers, SERVER.as_str())
}

pub fn version_from_headers(headers: &HeaderMap) -> crate::Result<&str> {
pub fn version_from_headers(headers: &Headers) -> crate::Result<&str> {
get_str_from_headers(headers, VERSION)
}

pub fn request_server_encrypted_from_headers(headers: &HeaderMap) -> crate::Result<bool> {
pub fn request_server_encrypted_from_headers(headers: &Headers) -> crate::Result<bool> {
get_from_headers(headers, REQUEST_SERVER_ENCRYPTED)
}

pub fn content_type_from_headers(headers: &HeaderMap) -> crate::Result<&str> {
pub fn content_type_from_headers(headers: &Headers) -> crate::Result<&str> {
get_str_from_headers(headers, http::header::CONTENT_TYPE.as_str())
}

pub fn item_count_from_headers(headers: &HeaderMap) -> crate::Result<u32> {
pub fn item_count_from_headers(headers: &Headers) -> crate::Result<u32> {
get_from_headers(headers, ITEM_COUNT)
}
Loading