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

Fix for parsing string headers #525

Merged
merged 2 commits into from
Jun 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 31 additions & 1 deletion codegen-test/model/rest-xml-extras.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ service RestXmlExtras {
XmlMapsFlattenedNestedXmlNamespace,
EnumKeys,
PrimitiveIntOpXml,
ChecksumRequired
ChecksumRequired,
StringHeader,
]
}

Expand Down Expand Up @@ -203,3 +204,32 @@ operation ChecksumRequired {
structure ChecksumRequiredInput {
field: String
}


@httpResponseTests([{
id: "DeserHeaderStringCommas",
code: 200,
documentation: """
Regression test for https://github.com/awslabs/aws-sdk-rust/issues/122
where `,` was eagerly used to split fields in cases where the input was not
a list.
""",
body: "",
headers: { "x-field": "a,b,c" },
params: {
field: "a,b,c"
},
protocol: "aws.protocols#restXml"
}])
@http(uri: "/StringHeader", method: "POST")
operation StringHeader {
output: StringHeaderOutput
}

structure StringHeaderOutput {
@httpHeader("x-field")
field: String,

@httpHeader("x-enum")
enumHeader: StringEnum
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,13 @@ class ResponseBindingGenerator(protocolConfig: ProtocolConfig, private val opera
*/
private fun RustWriter.deserializeFromHeader(targetType: Shape, memberShape: MemberShape) {
val rustType = symbolProvider.toSymbol(targetType).rustType().stripOuter<RustType.Option>()
// Normally, we go through a flow that looks for `,`s but that's wrong if the output
// is just a single string (which might include `,`s.).
// MediaType doesn't include `,` since it's base64, send that through the normal path
if (targetType is StringShape && !targetType.hasTrait<MediaTypeTrait>()) {
rust("#T::one_or_none(headers)", headerUtil)
return
}
val (coreType, coreShape) = if (targetType is CollectionShape) {
rustType.stripOuter<RustType.Container>() to model.expectShape(targetType.member.target)
} else {
Expand Down
19 changes: 18 additions & 1 deletion rust-runtime/smithy-http/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,25 @@ where
Ok(out)
}

/// 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>,
) -> Result<Option<T>, ParseError> {
let first = match values.next() {
Some(v) => v,
None => return Ok(None),
};
let value = std::str::from_utf8(first.as_bytes()).map_err(|_| ParseError)?;
match values.next() {
None => T::from_str(value.trim()).map_err(|_| ParseError).map(Some),
Some(_) => Err(ParseError),
}
}

/// Read one comma delimited value for `FromStr` types
pub fn read_one<T>(s: &[u8]) -> Result<(T, &[u8]), ParseError>
fn read_one<T>(s: &[u8]) -> Result<(T, &[u8]), ParseError>
where
T: FromStr,
{
Expand Down