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

add Method & StatusCode to azure_core #849

Closed
wants to merge 2 commits into from

Conversation

ctaggart
Copy link
Contributor

@ctaggart ctaggart commented Jun 23, 2022

This removes the last dependency on the http crate by adding a Method and StatusCode types. I'm interested in having compatibility with http-types and supporting surf in addition to reqwest. This draft wraps http-types implementations of Method and StatusCode, but exposes the API from http crate that we are using. There are a few different ways we could go here. Looking for feedback.

@ctaggart ctaggart marked this pull request as ready for review June 23, 2022 20:48
@@ -1,4 +1,5 @@
use std::ops::Deref;
use crate::error::{Error, ErrorKind};
use std::{ops::Deref, str::FromStr};

#[derive(PartialEq, Eq, Clone, Debug)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not Copy?


/// Construct a new `HttpClient` with the `reqwest` backend.
pub fn new_http_client() -> std::sync::Arc<dyn HttpClient> {
std::sync::Arc::new(::reqwest::Client::new())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::sync::Arc::new(::reqwest::Client::new())
std::sync::Arc::new(reqwest::Client::new())

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put these in a module named reqwest, so needed that prefix. Could rename the module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... or you could rename the 3rd party crate. use ::request as hyperium_request.

}

#[async_trait]
impl HttpClient for ::reqwest::Client {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
impl HttpClient for ::reqwest::Client {
impl HttpClient for reqwest::Client {

}

fn try_from_method(method: &crate::Method) -> crate::Result<::reqwest::Method> {
::reqwest::Method::from_str(method.as_ref()).map_kind(ErrorKind::DataConversion)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
::reqwest::Method::from_str(method.as_ref()).map_kind(ErrorKind::DataConversion)
reqwest::Method::from_str(method.as_ref()).map_kind(ErrorKind::DataConversion)

This should never happen. I wonder if we should have a wrapper error that says something like: if you ever see this, please report a bug.

Comment on lines +77 to +83
http_types::StatusCode::try_from(status)
.map_err(|_| {
Error::with_message(ErrorKind::DataConversion, || {
format!("invalid status code {status}")
})
})
.map(crate::StatusCode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use crate::StatusCode's TryFrom<u16> impl

Comment on lines +17 to +18
}
pub fn from_u16(value: u16) -> crate::Result<Self> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
pub fn from_u16(value: u16) -> crate::Result<Self> {
}
pub fn from_u16(value: u16) -> crate::Result<Self> {

pub const TOO_MANY_REQUESTS: StatusCode = StatusCode(http_types::StatusCode::TooManyRequests);
}

impl Default for StatusCode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I like this impl. OK does not logically feel like the "default" status code to me.

log = "0.4"
once_cell = "1.0"
# once_cell = "1.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove. http crate doesn’t have Mehod::MERGE.

Comment on lines +48 to +51
.parse::<u16>()
.map_err(|_| {
Error::message(ErrorKind::DataConversion, "invalid HTTP status code")
})?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think StatusCode has a TryFrom<&str> impl no?

@@ -40,7 +40,7 @@ impl Transaction {
for transaction_operation in self.transaction_operations.iter() {
s.push_str(&format!("--changeset_{}\nContent-Type: application/http\nContent-Transfer-Encoding: binary\n\n", self.change_set_uuid.hyphenated()));
s.push_str(&format!(
"{} {} HTTP/1.1\n",
"{:?} {} HTTP/1.1\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I like that method doesn't have a display. The string representations of the HTTP methods are cononical no?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string representations of the HTTP methods are cononical no?

Correct. As per RFC 9110, 9: Methods:

The method token is case-sensitive because it might be used as a gateway to object-based systems with case-sensitive method names. By convention, standardized methods are defined in all-uppercase US-ASCII letters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bigger question is do we want to use the http-types StatusCode directly. This wrapper shows what the differences are, where the gaps are.

Copy link
Contributor

@yoshuawuyts yoshuawuyts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like I'm missing some context about direction. Obviously as the lead author of http-types I'm thrilled by the prospect that we may want to use it for the azure SDK. But I want to make sure that we properly understand and discuss the tradeoffs of doing so. Because if we're going to adopt it, I want to make sure we're doing so for the right reasons.

For example: should we expose http-types types from the Rust Azure SDK's public API? If we do that, how do we feel about the governance? Are there any reasons beyond the code changes in this API why we may want to adopt this?

I think it would be worth laying out a vision of how you see the integration working out in the longer term. And then maybe we can discuss it offline here / perhaps bring it to a meeting to discuss further?

Comment on lines +28 to +38
pub const BAD_GATEWAY: StatusCode = StatusCode(http_types::StatusCode::BadGateway);
pub const GATEWAY_TIMEOUT: StatusCode = StatusCode(http_types::StatusCode::GatewayTimeout);
pub const INTERNAL_SERVER_ERROR: StatusCode =
StatusCode(http_types::StatusCode::InternalServerError);
pub const NOT_MODIFIED: StatusCode = StatusCode(http_types::StatusCode::NotModified);
pub const OK: StatusCode = StatusCode(http_types::StatusCode::Ok);
pub const REQUEST_TIMEOUT: StatusCode = StatusCode(http_types::StatusCode::RequestTimeout);
pub const SERVICE_UNAVAILABLE: StatusCode =
StatusCode(http_types::StatusCode::ServiceUnavailable);
pub const TOO_MANY_REQUESTS: StatusCode = StatusCode(http_types::StatusCode::TooManyRequests);
}
Copy link
Contributor

@yoshuawuyts yoshuawuyts Jun 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm so-so about this construct. In my opinion status codes are best represented in Rust as non-exhaustive enums; providing a balance between extensibility and enumerability.

I feel like we'd be better off either re-exporting http_types::StatusCode as-is, or if we don't want to rely on using http-types in our public API, either copying over the implementation - or creating a wrapper/type-alias to it.

@cataggar
Copy link
Member

We'll discuss this in our next meeting. A purpose of this PR was to show the differences between http and http-type implementations and start a discussion. I prefer http-types representations of both Method & StatusCode as enums.

https://docs.rs/http-types/latest/src/http_types/method.rs.html

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Method {

https://docs.rs/http-types/latest/src/http_types/status_code.rs.html

#[repr(u16)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum StatusCode {

http instead has consts for all the values.

#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Method(Inner);

enum Inner {
    Options,
    Get,

https://docs.rs/http/latest/src/http/status.rs.html

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StatusCode(NonZeroU16);

Differences

  • The biggest change needed for http-types is error the need for its error type to implement std::error::Error trouble mapping TryFrom errors #848 refactor: overhaul errors http-rs/http-types#395.
  • http::StatusCode has a fn as_u16, http-types is missing it and uses as u16 everywhere. The convenience function would be nice.
  • http::StatusCode has a default StatusCode::OK. Some of our Default implementations use this. Should be easy to figure out.
  • Because http-types uses an enum instead of constants, the name casing is different. Please BAD_GATEWAY with BadGateway and the 7 other status codes we use. This is just a few global replaces.

I created #852 with a subset of changes that leave http in place for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants