-
Notifications
You must be signed in to change notification settings - Fork 192
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
Create the HTTP Response
wrapper types
#3148
Conversation
HttpResponse
wrapper typesResponse
wrapper types
A new generated diff is ready to view.
A new doc preview is ready to view. |
@@ -277,15 +277,20 @@ class DefaultProtocolTestGenerator( | |||
writeInline("let expected_output =") | |||
instantiator.render(this, expectedShape, testCase.params) | |||
write(";") | |||
write("let mut http_response = #T::new()", RT.HttpResponseBuilder) | |||
rustTemplate( | |||
"let mut http_response = #{Response}::try_from(#{HttpResponseBuilder}::new()", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably just do try_into
at the bottom..but I guess this is a wide-open block anyway
@@ -428,7 +439,14 @@ mod test { | |||
.body(()) | |||
.unwrap(); | |||
let read = |name: &str, format: Format| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no worries either way but this is probably a smaller change with .try_into()
on all the requests instead
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've noticed the use of expect
and unwraps
in several places. While this might not pose a significant issue on the client side, it could be catastrophic if such code paths are executed on the server side. Are these instances confined to handling UTF-8 related headers only?
Additionally, what is our strategy for when http
reaches version 1.x? Will we revert to using the types defined by http
directly and convert our new Request
/Response
types into type aliases? Or do we plan on introducing into_http1x
?
let content_type = content_type.to_str().map_err(MissingContentTypeReason::ToStrError)?; | ||
content_type_header_classifier(content_type, expected_content_type) | ||
} else { | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'm curious as to why we don't generate an error when this function is called with expected_content_type
set to Some(...)
and yet the http::header::CONTENT_TYPE
is missing from the headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I don't know the answer to this.
.body(SdkBody::from(token)) | ||
.unwrap(), | ||
) | ||
.unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Could there be a scenario where HttpResponse::try_from
fails? If failure isn't possible, why did we implement try_from
rather than from
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try_from
fails when there a header value is not ASCII or when a header value is invalid UTF-8. My intention was for all these try_from(...).unwrap()
conversions to only take place in unit tests. In the actual code paths, we should be doing proper error handling for it.
.resolve("client::orchestrator::HttpRequest"), | ||
"HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) | ||
"HttpResponse" to RuntimeType.smithyRuntimeApiClient(runtimeConfig) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Considering that HttpResponse
is used on the server side as well, wouldn't it be more appropriate to place it in smithyRuntimeApi
instead of smithyRuntimeApiClient
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears like the types are also available without the client
feature flag, under the http
module. I wonder then why we don't use them directly instead of going through the client
module?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this makes sense to me. We could probably even get rid of the HttpRequest/HttpResponse types from th eorchestrator module at this point. I think it's not so high priority though, and we can always deprecate them later.
@@ -53,7 +53,7 @@ impl ClassifyRetry for SampleRetryClassifier { | |||
.and_then(|err| err.downcast_ref::<GetServerStatisticsError>()) | |||
{ | |||
if let Some(response) = ctx.response() { | |||
if response.status() == StatusCode::SERVICE_UNAVAILABLE { | |||
if response.status().as_u16() == StatusCode::SERVICE_UNAVAILABLE.as_u16() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: It would be nice to have an impl From<http::StatusCode>
for the response.status()
that we return. This would enable direct conversion from http::StatusCode
to the type of response.status()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. Added in 08f60b0.
@@ -26,6 +27,10 @@ pub enum RequestRejection { | |||
JsonDeserialize(#[from] aws_smithy_json::deserialize::error::DeserializeError), | |||
#[error("request does not adhere to modeled constraints: {0}")] | |||
ConstraintViolation(String), | |||
|
|||
/// Typically happens when the request has headers that are not valid UTF-8. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious, since this issue occurred previously during deserialization, and now it will simply occur earlier in the lifecycle, what was the error we returned at that time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We returned a HeaderParse
rejection:
Which encompasses other failure scenarios, like expecting a header to be present and it isn't.
|
||
/// Typically happens when the request has headers that are not valid UTF-8. | ||
#[error("failed to convert request: {0}")] | ||
HttpConversion(#[from] HttpError), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Do we have a test that checks for this new variant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently no test.
/// | ||
/// Depending on the internal storage type, this operation may be free or it may have an internal | ||
/// cost. | ||
pub fn into_http02x(self) -> Result<http0::Response<B>, HttpError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Wondering if we should call this try_into_http02x
?
Actually, do we ever return a HttpError
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For responses, the error isn't ever returned, but we keep the error as future proofing in the unlikely event that a http1 response can't easily be converted into a http02 response. I do like the idea of adding a try_
prefix to it. I will change that for the request and the response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed in bc10bcc.
extensions: Extensions, | ||
} | ||
|
||
impl<B> Response<B> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm uncertain about our handling of streaming responses; however, I presume this change won't impact that, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't make any changes to streaming.
|
||
/// An HTTP Response Type | ||
#[derive(Debug)] | ||
pub struct Response<B = SdkBody> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we implement Default
for this, or do you believe there is no need?
.resolve("client::orchestrator::HttpRequest"), | ||
"HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) | ||
"HttpResponse" to RuntimeType.smithyRuntimeApiClient(runtimeConfig) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears like the types are also available without the client
feature flag, under the http
module. I wonder then why we don't use them directly instead of going through the client
module?
@@ -26,6 +27,10 @@ pub enum RequestRejection { | |||
JsonDeserialize(#[from] aws_smithy_json::deserialize::error::DeserializeError), | |||
#[error("request does not adhere to modeled constraints: {0}")] | |||
ConstraintViolation(String), | |||
|
|||
/// Typically happens when the request has headers that are not valid UTF-8. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We returned a HeaderParse
rejection:
Which encompasses other failure scenarios, like expecting a header to be present and it isn't.
@@ -132,8 +130,8 @@ fn read_many<T>( | |||
/// Read exactly one or none from a headers iterator | |||
/// | |||
/// This function does not perform comma splitting like `read_many` | |||
pub fn one_or_none<T: FromStr>( | |||
mut values: ValueIter<'_, HeaderValue>, | |||
pub fn one_or_none<'a, T: FromStr>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(can't leave a comment on line 144). Shouldn't we now no longer need the std::str::from_utf8
call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general this file contains several .as_bytes
calls, and code that works with &[u8]
when I think it could directly work with &str
all the way through?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, line 144 looks pretty bad. I'll fix that one at the very least. It'll take a good bit of effort to fix the rest of the file though, it looks like.
Touched up in 85af035.
@@ -95,21 +96,28 @@ class ResponseBindingGeneratorTest { | |||
val testProject = TestWorkspace.testProject(symbolProvider) | |||
testProject.renderOperation() | |||
testProject.withModule(symbolProvider.moduleForShape(outputShape)) { | |||
unitTest( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we forgoing the unitTest
style of writing unit tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least in my opinion, the unitTest
function doesn't really buy me anything when I have a predetermined test name.
""" | ||
let #{RequestParts} { uri, headers, body, .. } = #{Request}::try_from(request)?.into_parts(); | ||
""", | ||
*preludeScope, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need preludeScope
here? We should add it to codegenScope
instead.
Better yet, I want rustTemplate
to have the prelude scope baked in, but that's a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes I want this too!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also want this, but I'll keep it out of this PR. Looks like I can remove the prelude from that rustTemplate call.
These wrapper types are forever, unfortunately. We will just change the insides of them to use http 1 at that time. You can read more in the RFC: https://github.com/smithy-lang/smithy-rs/blob/main/design/src/rfcs/rfc0037_http_wrapper.md |
A new generated diff is ready to view.
A new doc preview is ready to view. |
A new generated diff is ready to view.
A new doc preview is ready to view. |
For the same reason that the request wrapper types were created in #3059, this PR establishes the response wrapper types and refactors existing code to use them.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.