Loading codegen-test/model/rest-xml-extras.smithy +31 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,8 @@ service RestXmlExtras { XmlMapsFlattenedNestedXmlNamespace, EnumKeys, PrimitiveIntOpXml, ChecksumRequired ChecksumRequired, StringHeader, ] } Loading Loading @@ -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 } codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt +7 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading rust-runtime/smithy-http/src/header.rs +18 −1 Original line number Diff line number Diff line Loading @@ -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, { Loading Loading
codegen-test/model/rest-xml-extras.smithy +31 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,8 @@ service RestXmlExtras { XmlMapsFlattenedNestedXmlNamespace, EnumKeys, PrimitiveIntOpXml, ChecksumRequired ChecksumRequired, StringHeader, ] } Loading Loading @@ -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 }
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt +7 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading
rust-runtime/smithy-http/src/header.rs +18 −1 Original line number Diff line number Diff line Loading @@ -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, { Loading