package restxml import ( "bytes" "context" "fmt" "io" "github.com/aws/smithy-go" internalhttpbinding "github.com/aws/smithy-go/transport/http/protocol/internal/httpbinding" internalxml "github.com/aws/smithy-go/transport/http/protocol/internal/xml" internales "github.com/aws/smithy-go/internal/eventstream" "github.com/aws/smithy-go/traits" smithyhttp "github.com/aws/smithy-go/transport/http" ) // Protocol implements aws.protocols#restXml. type Protocol struct { eventstream *internales.Codec seropts func(*internalxml.ShapeSerializerOptions) } // ProtocolOptions configures aws.protocols#restXml. type ProtocolOptions struct{} var _ smithyhttp.ClientProtocol = (*Protocol)(nil) // New returns an instance of the aws.protocols#restXml protocol. If the // service schema carries an xmlNamespace trait, it is applied as the root // xmlns attribute on serialized request bodies. func New(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { var o ProtocolOptions for _, fn := range opts { fn(&o) } var xmlOpts func(*internalxml.ShapeSerializerOptions) if ns, ok := smithy.SchemaTrait[*traits.XMLNamespace](service.Schema); ok { xmlOpts = func(o *internalxml.ShapeSerializerOptions) { o.RootNamespaceURI = ns.URI o.RootNamespacePrefix = ns.Prefix } } return &Protocol{ eventstream: &internales.Codec{ Serializer: func() smithy.ShapeSerializer { if xmlOpts != nil { return internalxml.NewShapeSerializer(xmlOpts) } return internalxml.NewShapeSerializer() }, Deserializer: func(p []byte) smithy.ShapeDeserializer { return internalxml.NewShapeDeserializer(p) }, ContentType: "application/xml", }, seropts: xmlOpts, } } // ID identifies the protocol. func (*Protocol) ID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "restXml"} } // HasInitialEventMessage implements [smithyhttp.ClientProtocol]. func (*Protocol) HasInitialEventMessage() bool { return false } // SerializeEventMessage implements [smithyhttp.ClientProtocol]. func (p *Protocol) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { return p.eventstream.SerializeEventMessage(schema, variant, v, w) } // DeserializeEventMessage implements [smithyhttp.ClientProtocol]. func (p *Protocol) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { return p.eventstream.DeserializeEventMessage(schema, types, r) } // SerializeInitialRequest implements [smithyhttp.ClientProtocol]. func (p *Protocol) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { return p.eventstream.SerializeInitialRequest(schema, v, w) } // DeserializeInitialResponse implements [smithyhttp.ClientProtocol]. func (p *Protocol) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { return p.eventstream.DeserializeInitialResponse(schema, r, out) } // SerializeRequest serializes a request for restxml. func (p *Protocol) SerializeRequest( ctx context.Context, op *smithy.OperationSchema, in smithy.Serializable, req *smithyhttp.Request, ) error { serializer, err := internalhttpbinding.NewShapeSerializer(op.Schema, req, p.newSerializer()) if err != nil { return err } if op.Input != nil { in.Serialize(serializer) } contentType := "application/xml" if op.IsInputEventStream() { contentType = "application/vnd.amazon.eventstream" } if err := serializer.Build(in, contentType); err != nil { return fmt.Errorf("build request: %w", err) } return nil } // DeserializeResponse deserializes a response for restXml. func (p *Protocol) DeserializeResponse( ctx context.Context, op *smithy.OperationSchema, types *smithy.TypeRegistry, resp *smithyhttp.Response, out smithy.Deserializable, ) error { if resp.StatusCode < 200 || resp.StatusCode >= 300 { return p.deserializeError(types, resp) } if op.Output == nil { return nil } if so, ok := out.(smithy.StreamingOutput); ok { so.SetPayloadStream(resp.Body) deser := internalhttpbinding.NewShapeDeserializer(resp.Response, deser(nil, op.Output), nil) if err := out.Deserialize(deser); err != nil { return &smithy.DeserializationError{Err: err} } return nil } if op.IsOutputEventStream() { if op.IsInputEventStream() { if err := p.eventstream.RequireBidiHTTP2(resp.Proto, resp.ProtoMajor); err != nil { return err } } deser := internalhttpbinding.NewShapeDeserializer(resp.Response, deser(nil, op.Output), nil) if err := out.Deserialize(deser); err != nil { return &smithy.DeserializationError{Err: err} } return nil } payload, err := io.ReadAll(resp.Body) if err != nil { return &smithy.DeserializationError{Err: err} } deser := internalhttpbinding.NewShapeDeserializer(resp.Response, deser(payload, op.Output), payload) if err := out.Deserialize(deser); err != nil { return &smithy.DeserializationError{Err: err} } return nil } func (p *Protocol) newSerializer() *internalxml.ShapeSerializer { if p.seropts != nil { return internalxml.NewShapeSerializer(p.seropts) } return internalxml.NewShapeSerializer() } func (p *Protocol) deserializeError(types *smithy.TypeRegistry, resp *smithyhttp.Response) error { var buf bytes.Buffer if _, err := io.Copy(&buf, resp.Body); err != nil { return &smithy.DeserializationError{Err: fmt.Errorf("read error response body: %w", err)} } payload := buf.Bytes() errorCode, errorMessage, errorBody, err := internalxml.GetProtocolErrorInfo(payload) if err != nil { return &smithy.DeserializationError{Err: err} } perr, ok := types.DeserializableError(errorCode) if !ok { return &smithy.GenericAPIError{ Code: errorCode, Message: errorMessage, } } if len(errorBody) > 0 { deser := internalhttpbinding.NewShapeDeserializer(resp.Response, internalxml.NewShapeDeserializer(errorBody), payload) if err := perr.Deserialize(deser); err != nil { return &smithy.DeserializationError{Err: err} } } return perr } // returns a ShapeDeserializer over the (possibly empty) payload, wrapped in // a synthetic outer element whose name matches the operation output shape so // the schema-serde XML deserializer can open it uniformly func deser(payload []byte, out *smithy.Schema) smithy.ShapeDeserializer { if len(payload) != 0 { return internalxml.NewShapeDeserializer(payload) } name := "Response" if out != nil { if n := out.ID().Name; n != "" { name = n } } payload = []byte("<" + name + ">") return internalxml.NewShapeDeserializer(payload) }