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