1use std::sync::Arc;
9
10use crate::codec::{XmlCodec, XmlCodecSettings, XmlDeserializer};
11use crate::decode::{try_data, Document};
12use aws_smithy_schema::http_protocol::HttpBindingProtocol;
13use aws_smithy_schema::serde::SerdeError;
14use aws_smithy_schema::Schema;
15use aws_smithy_schema::ShapeId;
16use aws_smithy_types::config_bag::ConfigBag;
17use aws_smithy_types::date_time::Format as TimestampFormat;
18use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata};
19
20static PROTOCOL_ID: ShapeId = ShapeId::from_static("aws.protocols", "restXml", "");
21
22#[derive(Debug)]
24pub struct AwsRestXmlProtocol {
25 inner: HttpBindingProtocol<XmlCodec>,
26 settings: Arc<XmlCodecSettings>,
27 no_error_wrapping: bool,
29 service_xml_namespace: Option<(String, Option<String>)>,
34}
35
36impl AwsRestXmlProtocol {
37 pub fn new() -> Self {
49 let settings = XmlCodecSettings::builder()
50 .default_timestamp_format(TimestampFormat::DateTime)
51 .build();
52 let settings = Arc::new(settings);
53 let codec = XmlCodec::from_shared_settings(settings.clone());
54 Self {
55 inner: HttpBindingProtocol::new(PROTOCOL_ID, codec, "application/xml"),
56 settings,
57 no_error_wrapping: false,
58 service_xml_namespace: None,
59 }
60 }
61
62 pub fn with_no_error_wrapping(mut self, v: bool) -> Self {
68 self.no_error_wrapping = v;
69 self
70 }
71
72 pub fn no_error_wrapping(&self) -> bool {
75 self.no_error_wrapping
76 }
77
78 pub fn with_service_xml_namespace(
83 mut self,
84 uri: impl Into<String>,
85 prefix: Option<String>,
86 ) -> Self {
87 self.service_xml_namespace = Some((uri.into(), prefix));
88 self
89 }
90
91 pub fn deserialize_error_response<'a>(
95 &self,
96 body: &'a [u8],
97 ) -> Result<
98 (
99 ErrorMetadataBuilder,
100 Box<dyn aws_smithy_schema::serde::ShapeDeserializer + 'a>,
101 ),
102 SerdeError,
103 > {
104 let mut builder = ErrorMetadata::builder();
105
106 if self.no_error_wrapping {
107 let mut doc = Document::new(
109 std::str::from_utf8(body)
110 .map_err(|e| SerdeError::custom(format!("invalid UTF-8: {e}")))?,
111 );
112 let mut root = doc
113 .root_element()
114 .map_err(|e| SerdeError::custom(format!("{e}")))?;
115 while let Some(mut tag) = root.next_tag() {
116 match tag.start_el().local() {
117 "Code" => {
118 builder = builder.code(
119 try_data(&mut tag).map_err(|e| SerdeError::custom(format!("{e}")))?,
120 );
121 }
122 "Message" => {
123 builder = builder.message(
124 try_data(&mut tag).map_err(|e| SerdeError::custom(format!("{e}")))?,
125 );
126 }
127 _ => {}
128 }
129 }
130 let deser = XmlDeserializer::new(body, self.settings.clone());
132 Ok((builder, Box::new(deser)))
133 } else {
134 let mut doc = Document::new(
136 std::str::from_utf8(body)
137 .map_err(|e| SerdeError::custom(format!("invalid UTF-8: {e}")))?,
138 );
139 let mut root = doc
140 .root_element()
141 .map_err(|e| SerdeError::custom(format!("{e}")))?;
142 let mut error_fragment: Option<&'a [u8]> = None;
151 while let Some(mut tag) = root.next_tag() {
152 match tag.start_el().local() {
153 "Error" => {
154 let el_local = tag.start_el().local();
159 error_fragment = Some(XmlDeserializer::find_element_slice(body, el_local));
160 while let Some(mut error_field) = tag.next_tag() {
161 match error_field.start_el().local() {
162 "Code" => {
163 builder = builder.code(
164 try_data(&mut error_field)
165 .map_err(|e| SerdeError::custom(format!("{e}")))?,
166 );
167 }
168 "Message" => {
169 builder = builder.message(
170 try_data(&mut error_field)
171 .map_err(|e| SerdeError::custom(format!("{e}")))?,
172 );
173 }
174 _ => {}
175 }
176 }
177 }
178 "RequestId" => {
179 builder = builder.custom(
180 "request_id",
181 try_data(&mut tag).map_err(|e| SerdeError::custom(format!("{e}")))?,
182 );
183 }
184 _ => {}
185 }
186 }
187 let deser = match error_fragment {
193 Some(fragment) => XmlDeserializer::new(fragment, self.settings.clone()),
194 None => XmlDeserializer::new(body, self.settings.clone()),
195 };
196 Ok((builder, Box::new(deser)))
197 }
198 }
199}
200
201pub fn find_error_element_slice(body: &[u8]) -> &[u8] {
219 crate::codec::find_depth2_element_slice_by(body, |name| name == "Error").unwrap_or(body)
224}
225
226impl Default for AwsRestXmlProtocol {
227 fn default() -> Self {
228 Self::new()
229 }
230}
231
232impl aws_smithy_schema::protocol::ClientProtocolInner for AwsRestXmlProtocol {
233 type Request = aws_smithy_runtime_api::http::Request;
234 type Response = aws_smithy_runtime_api::http::Response;
235
236 fn protocol_id(&self) -> &ShapeId {
237 self.inner.protocol_id()
238 }
239
240 fn serialize_request(
241 &self,
242 input: &dyn aws_smithy_schema::serde::SerializableStruct,
243 input_schema: &Schema,
244 endpoint: &str,
245 cfg: &ConfigBag,
246 ) -> Result<aws_smithy_runtime_api::http::Request, aws_smithy_schema::serde::SerdeError> {
247 let payload_xml_name = input_schema.members().iter().find_map(|m| {
257 if m.http_payload().is_some()
258 && matches!(
259 m.shape_type(),
260 aws_smithy_schema::ShapeType::Structure | aws_smithy_schema::ShapeType::Union
261 )
262 {
263 m.xml_name().map(|n| n.value().to_owned())
264 } else {
265 None
266 }
267 });
268 let mut body = aws_smithy_schema::codec::Codec::create_serializer(self.inner.codec());
269 if let Some(name) = payload_xml_name {
270 body.set_next_root_xml_name(name);
271 }
272 if let Some((uri, prefix)) = &self.service_xml_namespace {
277 body.set_next_root_xml_namespace(uri.clone(), prefix.clone());
278 }
279 self.inner
280 .serialize_request_with_body(body, input, input_schema, endpoint, cfg)
281 }
282
283 fn deserialize_response<'a>(
284 &self,
285 response: &'a aws_smithy_runtime_api::http::Response,
286 output_schema: &Schema,
287 cfg: &ConfigBag,
288 ) -> Result<
289 Box<dyn aws_smithy_schema::serde::ShapeDeserializer + 'a>,
290 aws_smithy_schema::serde::SerdeError,
291 > {
292 self.inner
293 .deserialize_response(response, output_schema, cfg)
294 }
295
296 fn payload_codec(&self) -> Option<&dyn aws_smithy_schema::codec::DynCodec> {
297 self.inner.payload_codec()
298 }
299
300 fn update_endpoint(
301 &self,
302 request: &mut aws_smithy_runtime_api::http::Request,
303 endpoint: &aws_smithy_types::endpoint::Endpoint,
304 cfg: &ConfigBag,
305 ) -> Result<(), aws_smithy_schema::serde::SerdeError> {
306 self.inner.update_endpoint(request, endpoint, cfg)
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use aws_smithy_schema::protocol::ClientProtocolInner;
314 use aws_smithy_schema::serde::{SerializableStruct, ShapeSerializer};
315 use aws_smithy_schema::{shape_id, ShapeType};
316
317 use aws_smithy_schema::traits::HttpTrait;
318
319 static NAME_MEMBER: Schema =
320 Schema::new_member(shape_id!("test", "Op$name"), ShapeType::String, "name", 0);
321 static OP_SCHEMA: Schema = Schema::new_struct(
322 shape_id!("test", "OpRequest"),
323 ShapeType::Structure,
324 &[&NAME_MEMBER],
325 )
326 .with_original_name("OpRequest")
327 .with_http(HttpTrait::new("PUT", "/op", None));
328
329 struct TestInput;
330 impl SerializableStruct for TestInput {
331 fn serialize_members(
332 &self,
333 ser: &mut dyn ShapeSerializer,
334 ) -> Result<(), aws_smithy_schema::serde::SerdeError> {
335 ser.write_string(&NAME_MEMBER, "Alice")
336 }
337 }
338
339 #[test]
340 fn serialize_request_produces_xml_body() {
341 let protocol = AwsRestXmlProtocol::new();
342 let cfg = ConfigBag::base();
343 let request = protocol
344 .serialize_request(&TestInput, &OP_SCHEMA, "", &cfg)
345 .unwrap();
346
347 assert_eq!(request.method(), "PUT");
348 assert_eq!(request.uri(), "/op");
349 assert_eq!(
350 request.headers().get("content-type").unwrap(),
351 "application/xml"
352 );
353 let body = std::str::from_utf8(request.body().bytes().unwrap()).unwrap();
354 assert_eq!(body, "<OpRequest><name>Alice</name></OpRequest>");
355 }
356
357 #[test]
358 fn deserialize_response_round_trips() {
359 let protocol = AwsRestXmlProtocol::new();
360 let cfg = ConfigBag::base();
361 let body = b"<OpResponse><name>Bob</name></OpResponse>";
362 let response = aws_smithy_runtime_api::http::Response::new(
363 aws_smithy_runtime_api::http::StatusCode::try_from(200).unwrap(),
364 aws_smithy_types::body::SdkBody::from(&body[..]),
365 );
366
367 let mut deser = protocol
368 .deserialize_response(&response, &OP_SCHEMA, &cfg)
369 .unwrap();
370
371 let mut name = String::new();
372 deser
373 .read_struct(&OP_SCHEMA, &mut |member, d| {
374 if member.member_name() == Some("name") {
375 name = d.read_string(member)?;
376 }
377 Ok(())
378 })
379 .unwrap();
380 assert_eq!(name, "Bob");
381 }
382
383 #[test]
384 fn deserialize_error_wrapped() {
385 let protocol = AwsRestXmlProtocol::new();
386 let body = b"<ErrorResponse><Error><Type>Sender</Type><Code>InvalidGreeting</Code><Message>Hi</Message><Greeting>Howdy</Greeting></Error><RequestId>req-1</RequestId></ErrorResponse>";
387
388 let (builder, mut deser) = protocol.deserialize_error_response(body).unwrap();
389 let meta = builder.build();
390 assert_eq!(meta.code(), Some("InvalidGreeting"));
391 assert_eq!(meta.message(), Some("Hi"));
392 assert_eq!(meta.extra("request_id"), Some("req-1"));
393
394 let mut greeting = String::new();
396 deser
397 .read_struct(&OP_SCHEMA, &mut |member, d| {
398 if member.member_name() == Some("name") {
399 greeting = d.read_string(member)?;
400 }
401 Ok(())
402 })
403 .unwrap();
404 }
407
408 #[test]
409 fn deserialize_error_unwrapped() {
410 let protocol = AwsRestXmlProtocol::new().with_no_error_wrapping(true);
411 let body =
412 b"<Error><Code>NotFound</Code><Message>Gone</Message><Detail>extra</Detail></Error>";
413
414 let (builder, _deser) = protocol.deserialize_error_response(body).unwrap();
415 let meta = builder.build();
416 assert_eq!(meta.code(), Some("NotFound"));
417 assert_eq!(meta.message(), Some("Gone"));
418 }
419
420 #[test]
421 fn deserialize_error_wrapped_ignores_literal_error_in_cdata() {
422 let protocol = AwsRestXmlProtocol::new();
436 let body = b"<ErrorResponse>\
437 <Description><![CDATA[<Error><Code>FAKE</Code></Error>]]></Description>\
438 <Error><Code>RealCode</Code><Message>Real message</Message></Error>\
439 <RequestId>req-1</RequestId>\
440 </ErrorResponse>";
441
442 let (builder, _deser) = protocol.deserialize_error_response(body).unwrap();
443 let meta = builder.build();
444 assert_eq!(meta.code(), Some("RealCode"));
447 assert_eq!(meta.message(), Some("Real message"));
448 assert_eq!(meta.extra("request_id"), Some("req-1"));
449 }
450
451 #[test]
455 fn find_error_element_slice_strips_wrapped_envelope() {
456 let body =
457 b"<ErrorResponse><Error><Code>X</Code></Error><RequestId>r</RequestId></ErrorResponse>";
458 let slice = find_error_element_slice(body);
459 assert_eq!(slice, b"<Error><Code>X</Code></Error>");
460 }
461
462 #[test]
463 fn find_error_element_slice_handles_xmlns_on_inner_error() {
464 let body = br#"<ErrorResponse><Error xmlns="http://example.com/"><Code>X</Code></Error></ErrorResponse>"#;
469 let slice = find_error_element_slice(body);
470 assert_eq!(
471 slice,
472 br#"<Error xmlns="http://example.com/"><Code>X</Code></Error>"#
473 );
474 }
475
476 #[test]
477 fn find_error_element_slice_returns_body_when_root_is_error() {
478 let body = b"<Error><Code>X</Code><Message>m</Message></Error>";
482 let slice = find_error_element_slice(body);
483 assert_eq!(slice, body);
484 }
485
486 #[test]
487 fn find_error_element_slice_falls_back_for_missing_error() {
488 let body = b"<SomethingElse><Code>X</Code></SomethingElse>";
492 let slice = find_error_element_slice(body);
493 assert_eq!(slice, body);
494 }
495
496 #[test]
497 fn find_error_element_slice_falls_back_for_invalid_xml() {
498 let body = b"not xml at all";
500 let slice = find_error_element_slice(body);
501 assert_eq!(slice, body);
502 }
503
504 #[test]
505 fn find_error_element_slice_falls_back_for_invalid_utf8() {
506 let body = &[0xFFu8, 0xFE, 0xFD][..];
509 let slice = find_error_element_slice(body);
510 assert_eq!(slice, body);
511 }
512}