1// +build codegen 2 3package api 4 5import ( 6 "fmt" 7 "io" 8 "strings" 9 "text/template" 10) 11 12func renderEventStreamAPI(w io.Writer, op *Operation) error { 13 // Imports needed by the EventStream APIs. 14 op.API.AddImport("fmt") 15 op.API.AddImport("bytes") 16 op.API.AddImport("io") 17 op.API.AddImport("time") 18 op.API.AddSDKImport("aws") 19 op.API.AddSDKImport("aws/awserr") 20 op.API.AddSDKImport("aws/request") 21 op.API.AddSDKImport("private/protocol/eventstream") 22 op.API.AddSDKImport("private/protocol/eventstream/eventstreamapi") 23 24 w.Write([]byte(` 25var _ awserr.Error 26`)) 27 28 return eventStreamAPITmpl.Execute(w, op) 29} 30 31// Template for an EventStream API Shape that will provide read/writing events 32// across the EventStream. This is a special shape that's only public members 33// are the Events channel and a Close and Err method. 34// 35// Executed in the context of a Shape. 36var eventStreamAPITmpl = template.Must( 37 template.New("eventStreamAPITmplDef"). 38 Funcs(template.FuncMap{ 39 "unexported": func(v string) string { 40 return strings.ToLower(string(v[0])) + v[1:] 41 }, 42 }). 43 Parse(eventStreamAPITmplDef), 44) 45 46const eventStreamAPITmplDef = ` 47{{- $esapi := $.EventStreamAPI }} 48{{- $outputStream := $esapi.OutputStream }} 49{{- $inputStream := $esapi.InputStream }} 50 51// {{ $esapi.Name }} provides the event stream handling for the {{ $.ExportedName }}. 52// 53// For testing and mocking the event stream this type should be initialized via 54// the New{{ $esapi.Name }} constructor function. Using the functional options 55// to pass in nested mock behavior. 56type {{ $esapi.Name }} struct { 57 {{- if $inputStream }} 58 59 // Writer is the EventStream writer for the {{ $inputStream.Name }} 60 // events. This value is automatically set by the SDK when the API call is made 61 // Use this member when unit testing your code with the SDK to mock out the 62 // EventStream Writer. 63 // 64 // Must not be nil. 65 Writer {{ $inputStream.StreamWriterAPIName }} 66 67 inputWriter io.WriteCloser 68 {{- if eq .API.Metadata.Protocol "json" }} 69 input {{ $.InputRef.GoType }} 70 {{- end }} 71 {{- end }} 72 73 {{- if $outputStream }} 74 75 // Reader is the EventStream reader for the {{ $outputStream.Name }} 76 // events. This value is automatically set by the SDK when the API call is made 77 // Use this member when unit testing your code with the SDK to mock out the 78 // EventStream Reader. 79 // 80 // Must not be nil. 81 Reader {{ $outputStream.StreamReaderAPIName }} 82 83 outputReader io.ReadCloser 84 {{- if eq .API.Metadata.Protocol "json" }} 85 output {{ $.OutputRef.GoType }} 86 {{- end }} 87 {{- end }} 88 89 {{- if $esapi.Legacy }} 90 91 // StreamCloser is the io.Closer for the EventStream connection. For HTTP 92 // EventStream this is the response Body. The stream will be closed when 93 // the Close method of the EventStream is called. 94 StreamCloser io.Closer 95 {{- end }} 96 97 done chan struct{} 98 closeOnce sync.Once 99 err *eventstreamapi.OnceError 100} 101 102// New{{ $esapi.Name }} initializes an {{ $esapi.Name }}. 103// This function should only be used for testing and mocking the {{ $esapi.Name }} 104// stream within your application. 105{{- if $inputStream }} 106// 107// The Writer member must be set before writing events to the stream. 108{{- end }} 109{{- if $outputStream }} 110// 111// The Reader member must be set before reading events from the stream. 112{{- end }} 113{{- if $esapi.Legacy }} 114// 115// The StreamCloser member should be set to the underlying io.Closer, 116// (e.g. http.Response.Body), that will be closed when the stream Close method 117// is called. 118{{- end }} 119// 120// es := New{{ $esapi.Name }}(func(o *{{ $esapi.Name}}{ 121{{- if $inputStream }} 122// es.Writer = myMockStreamWriter 123{{- end }} 124{{- if $outputStream }} 125// es.Reader = myMockStreamReader 126{{- end }} 127{{- if $esapi.Legacy }} 128// es.StreamCloser = myMockStreamCloser 129{{- end }} 130// }) 131func New{{ $esapi.Name }}(opts ...func(*{{ $esapi.Name}})) *{{ $esapi.Name }} { 132 es := &{{ $esapi.Name }} { 133 done: make(chan struct{}), 134 err: eventstreamapi.NewOnceError(), 135 } 136 137 for _, fn := range opts { 138 fn(es) 139 } 140 141 return es 142} 143 144{{- if $esapi.Legacy }} 145 146 func (es *{{ $esapi.Name }}) setStreamCloser(r *request.Request) { 147 es.StreamCloser = r.HTTPResponse.Body 148 } 149{{- end }} 150 151func (es *{{ $esapi.Name }}) runOnStreamPartClose(r *request.Request) { 152 if es.done == nil { 153 return 154 } 155 go es.waitStreamPartClose() 156 157} 158 159func (es *{{ $esapi.Name }}) waitStreamPartClose() { 160 {{- if $inputStream }} 161 var inputErrCh <-chan struct{} 162 if v, ok := es.Writer.(interface{ErrorSet() <-chan struct{}}); ok { 163 inputErrCh = v.ErrorSet() 164 } 165 {{- end }} 166 {{- if $outputStream }} 167 var outputErrCh <-chan struct{} 168 if v, ok := es.Reader.(interface{ErrorSet() <-chan struct{}}); ok { 169 outputErrCh = v.ErrorSet() 170 } 171 var outputClosedCh <- chan struct{} 172 if v, ok := es.Reader.(interface{Closed() <-chan struct{}}); ok { 173 outputClosedCh = v.Closed() 174 } 175 {{- end }} 176 177 select { 178 case <-es.done: 179 180 {{- if $inputStream }} 181 case <-inputErrCh: 182 es.err.SetError(es.Writer.Err()) 183 es.Close() 184 {{- end }} 185 186 {{- if $outputStream }} 187 case <-outputErrCh: 188 es.err.SetError(es.Reader.Err()) 189 es.Close() 190 case <-outputClosedCh: 191 if err := es.Reader.Err(); err != nil { 192 es.err.SetError(es.Reader.Err()) 193 } 194 es.Close() 195 {{- end }} 196 } 197} 198 199{{- if $inputStream }} 200 201 {{- if eq .API.Metadata.Protocol "json" }} 202 203 func {{ $esapi.StreamInputEventTypeGetterName }}(event {{ $inputStream.EventGroupName }}) (string, error) { 204 if _, ok := event.({{ $.InputRef.GoType }}); ok { 205 return "initial-request", nil 206 } 207 return {{ $inputStream.StreamEventTypeGetterName }}(event) 208 } 209 {{- end }} 210 211 func (es *{{ $esapi.Name }}) setupInputPipe(r *request.Request) { 212 inputReader, inputWriter := io.Pipe() 213 r.SetStreamingBody(inputReader) 214 es.inputWriter = inputWriter 215 } 216 217 // Closes the input-pipe writer 218 func (es *{{ $esapi.Name }}) closeInputPipe() error { 219 if es.inputWriter != nil { 220 return es.inputWriter.Close() 221 } 222 return nil 223 } 224 225 // Send writes the event to the stream blocking until the event is written. 226 // Returns an error if the event was not written. 227 // 228 // These events are: 229 // {{ range $_, $event := $inputStream.Events }} 230 // * {{ $event.Shape.ShapeName }} 231 {{- end }} 232 func (es *{{ $esapi.Name }}) Send(ctx aws.Context, event {{ $inputStream.EventGroupName }}) error { 233 return es.Writer.Send(ctx, event) 234 } 235 236 func (es *{{ $esapi.Name }}) runInputStream(r *request.Request) { 237 var opts []func(*eventstream.Encoder) 238 if r.Config.Logger != nil && r.Config.LogLevel.Matches(aws.LogDebugWithEventStreamBody) { 239 opts = append(opts, eventstream.EncodeWithLogger(r.Config.Logger)) 240 } 241 var encoder eventstreamapi.Encoder = eventstream.NewEncoder(es.inputWriter, opts...) 242 243 var closer aws.MultiCloser 244 {{- if $.ShouldSignRequestBody }} 245 {{- $_ := $.API.AddSDKImport "aws/signer/v4" }} 246 sigSeed, err := v4.GetSignedRequestSignature(r.HTTPRequest) 247 if err != nil { 248 r.Error = awserr.New(request.ErrCodeSerialization, 249 "unable to get initial request's signature", err) 250 return 251 } 252 signer := eventstreamapi.NewSignEncoder( 253 v4.NewStreamSigner(r.ClientInfo.SigningRegion, r.ClientInfo.SigningName, 254 sigSeed, r.Config.Credentials), 255 encoder, 256 ) 257 encoder = signer 258 closer = append(closer, signer) 259 {{- end }} 260 closer = append(closer, es.inputWriter) 261 262 eventWriter := eventstreamapi.NewEventWriter(encoder, 263 protocol.HandlerPayloadMarshal{ 264 Marshalers: r.Handlers.BuildStream, 265 }, 266 {{- if eq .API.Metadata.Protocol "json" }} 267 {{ $esapi.StreamInputEventTypeGetterName }}, 268 {{- else }} 269 {{ $inputStream.StreamEventTypeGetterName }}, 270 {{- end }} 271 ) 272 273 es.Writer = &{{ $inputStream.StreamWriterImplName }}{ 274 StreamWriter: eventstreamapi.NewStreamWriter(eventWriter, closer), 275 } 276 } 277 278 {{- if eq .API.Metadata.Protocol "json" }} 279 func (es *{{ $esapi.Name }}) sendInitialEvent(r *request.Request) { 280 if err := es.Send(es.input); err != nil { 281 r.Error = err 282 } 283 } 284 {{- end }} 285{{- end }} 286 287{{- if $outputStream }} 288 {{- if eq .API.Metadata.Protocol "json" }} 289 290 type {{ $esapi.StreamOutputUnmarshalerForEventName }} struct { 291 unmarshalerForEvent func(string) (eventstreamapi.Unmarshaler, error) 292 output {{ $.OutputRef.GoType }} 293 } 294 func (e {{ $esapi.StreamOutputUnmarshalerForEventName }}) UnmarshalerForEventName(eventType string) (eventstreamapi.Unmarshaler, error) { 295 if eventType == "initial-response" { 296 return e.output, nil 297 } 298 return e.unmarshalerForEvent(eventType) 299 } 300 {{- end }} 301 302 // Events returns a channel to read events from. 303 // 304 // These events are: 305 // {{ range $_, $event := $outputStream.Events }} 306 // * {{ $event.Shape.ShapeName }} 307 {{- end }} 308 // * {{ $outputStream.StreamUnknownEventName }} 309 func (es *{{ $esapi.Name }}) Events() <-chan {{ $outputStream.EventGroupName }} { 310 return es.Reader.Events() 311 } 312 313 func (es *{{ $esapi.Name }}) runOutputStream(r *request.Request) { 314 var opts []func(*eventstream.Decoder) 315 if r.Config.Logger != nil && r.Config.LogLevel.Matches(aws.LogDebugWithEventStreamBody) { 316 opts = append(opts, eventstream.DecodeWithLogger(r.Config.Logger)) 317 } 318 319 unmarshalerForEvent := {{ $outputStream.StreamUnmarshalerForEventName }}{ 320 metadata: protocol.ResponseMetadata{ 321 StatusCode: r.HTTPResponse.StatusCode, 322 RequestID: r.RequestID, 323 }, 324 }.UnmarshalerForEventName 325 {{- if eq .API.Metadata.Protocol "json" }} 326 unmarshalerForEvent = {{ $esapi.StreamOutputUnmarshalerForEventName }}{ 327 unmarshalerForEvent: unmarshalerForEvent, 328 output: es.output, 329 }.UnmarshalerForEventName 330 {{- end }} 331 332 decoder := eventstream.NewDecoder(r.HTTPResponse.Body, opts...) 333 eventReader := eventstreamapi.NewEventReader(decoder, 334 protocol.HandlerPayloadUnmarshal{ 335 Unmarshalers: r.Handlers.UnmarshalStream, 336 }, 337 unmarshalerForEvent, 338 ) 339 340 es.outputReader = r.HTTPResponse.Body 341 es.Reader = {{ $outputStream.StreamReaderImplConstructorName }}(eventReader) 342 } 343 344 {{- if eq .API.Metadata.Protocol "json" }} 345 func (es *{{ $esapi.Name }}) recvInitialEvent(r *request.Request) { 346 // Wait for the initial response event, which must be the first 347 // event to be received from the API. 348 select { 349 case event, ok := <- es.Events(): 350 if !ok { 351 return 352 } 353 354 v, ok := event.({{ $.OutputRef.GoType }}) 355 if !ok || v == nil { 356 r.Error = awserr.New( 357 request.ErrCodeSerialization, 358 fmt.Sprintf("invalid event, %T, expect %T, %v", 359 event, ({{ $.OutputRef.GoType }})(nil), v), 360 nil, 361 ) 362 return 363 } 364 365 *es.output = *v 366 es.output.{{ $.EventStreamAPI.OutputMemberName }} = es 367 } 368 } 369 {{- end }} 370{{- end }} 371 372// Close closes the stream. This will also cause the stream to be closed. 373// Close must be called when done using the stream API. Not calling Close 374// may result in resource leaks. 375{{- if $inputStream }} 376// 377// Will close the underlying EventStream writer, and no more events can be 378// sent. 379{{- end }} 380{{- if $outputStream }} 381// 382// You can use the closing of the Reader's Events channel to terminate your 383// application's read from the API's stream. 384{{- end }} 385// 386func (es *{{ $esapi.Name }}) Close() (err error) { 387 es.closeOnce.Do(es.safeClose) 388 return es.Err() 389} 390 391func (es *{{ $esapi.Name }}) safeClose() { 392 if es.done != nil { 393 close(es.done) 394 } 395 396 {{- if $inputStream }} 397 398 t := time.NewTicker(time.Second) 399 defer t.Stop() 400 writeCloseDone := make(chan error) 401 go func() { 402 if err := es.Writer.Close(); err != nil { 403 es.err.SetError(err) 404 } 405 close(writeCloseDone) 406 }() 407 select { 408 case <-t.C: 409 case <-writeCloseDone: 410 } 411 if err := es.closeInputPipe(); err != nil { 412 es.err.SetError(err) 413 } 414 {{- end }} 415 416 {{- if $outputStream }} 417 418 es.Reader.Close() 419 if es.outputReader != nil { 420 es.outputReader.Close() 421 } 422 {{- end }} 423 424 {{- if $esapi.Legacy }} 425 426 es.StreamCloser.Close() 427 {{- end }} 428} 429 430// Err returns any error that occurred while reading or writing EventStream 431// Events from the service API's response. Returns nil if there were no errors. 432func (es *{{ $esapi.Name }}) Err() error { 433 if err := es.err.Err(); err != nil { 434 return err 435 } 436 437 {{- if $inputStream }} 438 if err := es.Writer.Err(); err != nil { 439 return err 440 } 441 {{- end }} 442 443 {{- if $outputStream }} 444 if err := es.Reader.Err(); err != nil { 445 return err 446 } 447 {{- end }} 448 449 return nil 450} 451` 452 453func renderEventStreamShape(w io.Writer, s *Shape) error { 454 // Imports needed by the EventStream APIs. 455 s.API.AddImport("fmt") 456 s.API.AddImport("bytes") 457 s.API.AddImport("io") 458 s.API.AddImport("sync") 459 s.API.AddSDKImport("aws") 460 s.API.AddSDKImport("aws/awserr") 461 s.API.AddSDKImport("private/protocol/eventstream") 462 s.API.AddSDKImport("private/protocol/eventstream/eventstreamapi") 463 464 return eventStreamShapeTmpl.Execute(w, s) 465} 466 467var eventStreamShapeTmpl = func() *template.Template { 468 t := template.Must( 469 template.New("eventStreamShapeTmplDef"). 470 Parse(eventStreamShapeTmplDef), 471 ) 472 template.Must( 473 t.AddParseTree( 474 "eventStreamShapeWriterTmpl", eventStreamShapeWriterTmpl.Tree), 475 ) 476 template.Must( 477 t.AddParseTree( 478 "eventStreamShapeReaderTmpl", eventStreamShapeReaderTmpl.Tree), 479 ) 480 481 return t 482}() 483 484const eventStreamShapeTmplDef = ` 485{{- $eventStream := $.EventStream }} 486{{- $eventStreamEventGroup := printf "%sEvent" $eventStream.Name }} 487 488// {{ $eventStreamEventGroup }} groups together all EventStream 489// events writes for {{ $eventStream.Name }}. 490// 491// These events are: 492// {{ range $_, $event := $eventStream.Events }} 493// * {{ $event.Shape.ShapeName }} 494{{- end }} 495type {{ $eventStreamEventGroup }} interface { 496 event{{ $eventStream.Name }}() 497 eventstreamapi.Marshaler 498 eventstreamapi.Unmarshaler 499} 500 501{{- if $.IsInputEventStream }} 502 {{- template "eventStreamShapeWriterTmpl" $ }} 503{{- end }} 504 505{{- if $.IsOutputEventStream }} 506 {{- template "eventStreamShapeReaderTmpl" $ }} 507{{- end }} 508` 509 510// EventStreamHeaderTypeMap provides the mapping of a EventStream Header's 511// Value type to the shape reference's member type. 512type EventStreamHeaderTypeMap struct { 513 Header string 514 Member string 515} 516 517// Returns if the event has any members which are not the event's blob payload, 518// nor a header. 519func eventHasNonBlobPayloadMembers(s *Shape) bool { 520 num := len(s.MemberRefs) 521 for _, ref := range s.MemberRefs { 522 if ref.IsEventHeader || (ref.IsEventPayload && (ref.Shape.Type == "blob" || ref.Shape.Type == "string")) { 523 num-- 524 } 525 } 526 return num > 0 527} 528 529func setEventHeaderValueForType(s *Shape, memVar string) string { 530 switch s.Type { 531 case "blob": 532 return fmt.Sprintf("eventstream.BytesValue(%s)", memVar) 533 case "string": 534 return fmt.Sprintf("eventstream.StringValue(*%s)", memVar) 535 case "boolean": 536 return fmt.Sprintf("eventstream.BoolValue(*%s)", memVar) 537 case "byte": 538 return fmt.Sprintf("eventstream.Int8Value(int8(*%s))", memVar) 539 case "short": 540 return fmt.Sprintf("eventstream.Int16Value(int16(*%s))", memVar) 541 case "integer": 542 return fmt.Sprintf("eventstream.Int32Value(int32(*%s))", memVar) 543 case "long": 544 return fmt.Sprintf("eventstream.Int64Value(*%s)", memVar) 545 case "float": 546 return fmt.Sprintf("eventstream.Float32Value(float32(*%s))", memVar) 547 case "double": 548 return fmt.Sprintf("eventstream.Float64Value(*%s)", memVar) 549 case "timestamp": 550 return fmt.Sprintf("eventstream.TimestampValue(*%s)", memVar) 551 default: 552 panic(fmt.Sprintf("value type %s not supported for event headers, %s", s.Type, s.ShapeName)) 553 } 554} 555 556func shapeMessageType(s *Shape) string { 557 if s.Exception { 558 return "eventstreamapi.ExceptionMessageType" 559 } 560 return "eventstreamapi.EventMessageType" 561} 562 563var eventStreamEventShapeTmplFuncs = template.FuncMap{ 564 "EventStreamHeaderTypeMap": func(ref *ShapeRef) EventStreamHeaderTypeMap { 565 switch ref.Shape.Type { 566 case "boolean": 567 return EventStreamHeaderTypeMap{Header: "bool", Member: "bool"} 568 case "byte": 569 return EventStreamHeaderTypeMap{Header: "int8", Member: "int64"} 570 case "short": 571 return EventStreamHeaderTypeMap{Header: "int16", Member: "int64"} 572 case "integer": 573 return EventStreamHeaderTypeMap{Header: "int32", Member: "int64"} 574 case "long": 575 return EventStreamHeaderTypeMap{Header: "int64", Member: "int64"} 576 case "timestamp": 577 return EventStreamHeaderTypeMap{Header: "time.Time", Member: "time.Time"} 578 case "blob": 579 return EventStreamHeaderTypeMap{Header: "[]byte", Member: "[]byte"} 580 case "string": 581 return EventStreamHeaderTypeMap{Header: "string", Member: "string"} 582 case "uuid": 583 return EventStreamHeaderTypeMap{Header: "[]byte", Member: "[]byte"} 584 default: 585 panic("unsupported EventStream header type, " + ref.Shape.Type) 586 } 587 }, 588 "EventHeaderValueForType": setEventHeaderValueForType, 589 "ShapeMessageType": shapeMessageType, 590 "HasNonBlobPayloadMembers": eventHasNonBlobPayloadMembers, 591} 592 593// Template for an EventStream Event shape. This is a normal API shape that is 594// decorated as an EventStream Event. 595// 596// Executed in the context of a Shape. 597var eventStreamEventShapeTmpl = template.Must(template.New("eventStreamEventShapeTmpl"). 598 Funcs(eventStreamEventShapeTmplFuncs).Parse(` 599{{ range $_, $eventStream := $.EventFor }} 600 // The {{ $.ShapeName }} is and event in the {{ $eventStream.Name }} group of events. 601 func (s *{{ $.ShapeName }}) event{{ $eventStream.Name }}() {} 602{{ end }} 603 604// UnmarshalEvent unmarshals the EventStream Message into the {{ $.ShapeName }} value. 605// This method is only used internally within the SDK's EventStream handling. 606func (s *{{ $.ShapeName }}) UnmarshalEvent( 607 payloadUnmarshaler protocol.PayloadUnmarshaler, 608 msg eventstream.Message, 609) error { 610 {{- range $memName, $memRef := $.MemberRefs }} 611 {{- if $memRef.IsEventHeader }} 612 if hv := msg.Headers.Get("{{ $memName }}"); hv != nil { 613 {{ $types := EventStreamHeaderTypeMap $memRef -}} 614 v := hv.Get().({{ $types.Header }}) 615 {{- if ne $types.Header $types.Member }} 616 m := {{ $types.Member }}(v) 617 s.{{ $memName }} = {{ if $memRef.UseIndirection }}&{{ end }}m 618 {{- else }} 619 s.{{ $memName }} = {{ if $memRef.UseIndirection }}&{{ end }}v 620 {{- end }} 621 } 622 {{- else if (and ($memRef.IsEventPayload) (eq $memRef.Shape.Type "blob")) }} 623 s.{{ $memName }} = make([]byte, len(msg.Payload)) 624 copy(s.{{ $memName }}, msg.Payload) 625 {{- else if (and ($memRef.IsEventPayload) (eq $memRef.Shape.Type "string")) }} 626 s.{{ $memName }} = aws.String(string(msg.Payload)) 627 {{- end }} 628 {{- end }} 629 {{- if HasNonBlobPayloadMembers $ }} 630 if err := payloadUnmarshaler.UnmarshalPayload( 631 bytes.NewReader(msg.Payload), s, 632 ); err != nil { 633 return err 634 } 635 {{- end }} 636 return nil 637} 638 639// MarshalEvent marshals the type into an stream event value. This method 640// should only used internally within the SDK's EventStream handling. 641func (s *{{ $.ShapeName}}) MarshalEvent(pm protocol.PayloadMarshaler) (msg eventstream.Message, err error) { 642 msg.Headers.Set(eventstreamapi.MessageTypeHeader, eventstream.StringValue({{ ShapeMessageType $ }})) 643 644 {{- range $memName, $memRef := $.MemberRefs }} 645 {{- if $memRef.IsEventHeader }} 646 {{ $memVar := printf "s.%s" $memName -}} 647 {{ $typedMem := EventHeaderValueForType $memRef.Shape $memVar -}} 648 msg.Headers.Set("{{ $memName }}", {{ $typedMem }}) 649 {{- else if (and ($memRef.IsEventPayload) (eq $memRef.Shape.Type "blob")) }} 650 msg.Headers.Set(":content-type", eventstream.StringValue("application/octet-stream")) 651 msg.Payload = s.{{ $memName }} 652 {{- else if (and ($memRef.IsEventPayload) (eq $memRef.Shape.Type "string")) }} 653 msg.Payload = []byte(aws.StringValue(s.{{ $memName }})) 654 {{- end }} 655 {{- end }} 656 {{- if HasNonBlobPayloadMembers $ }} 657 var buf bytes.Buffer 658 if err = pm.MarshalPayload(&buf, s); err != nil { 659 return eventstream.Message{}, err 660 } 661 msg.Payload = buf.Bytes() 662 {{- end }} 663 return msg, err 664} 665`)) 666