stack.go

  1package middleware
  2
  3import (
  4	"context"
  5	"io"
  6	"strings"
  7)
  8
  9// Stack provides protocol and transport agnostic set of middleware split into
 10// distinct steps. Steps have specific transitions between them, that are
 11// managed by the individual step.
 12//
 13// Steps are composed as middleware around the underlying handler in the
 14// following order:
 15//
 16//   Initialize -> Serialize -> Build -> Finalize -> Deserialize -> Handler
 17//
 18// Any middleware within the chain may choose to stop and return an error or
 19// response. Since the middleware decorate the handler like a call stack, each
 20// middleware will receive the result of the next middleware in the chain.
 21// Middleware that does not need to react to an input, or result must forward
 22// along the input down the chain, or return the result back up the chain.
 23//
 24//   Initialize <- Serialize -> Build -> Finalize <- Deserialize <- Handler
 25type Stack struct {
 26	// Initialize prepares the input, and sets any default parameters as
 27	// needed, (e.g. idempotency token, and presigned URLs).
 28	//
 29	// Takes Input Parameters, and returns result or error.
 30	//
 31	// Receives result or error from Serialize step.
 32	Initialize *InitializeStep
 33
 34	// Serialize serializes the prepared input into a data structure that can be consumed
 35	// by the target transport's message, (e.g. REST-JSON serialization)
 36	//
 37	// Converts Input Parameters into a Request, and returns the result or error.
 38	//
 39	// Receives result or error from Build step.
 40	Serialize *SerializeStep
 41
 42	// Build adds additional metadata to the serialized transport message
 43	// (e.g. HTTP's Content-Length header, or body checksum). Decorations and
 44	// modifications to the message should be copied to all message attempts.
 45	//
 46	// Takes Request, and returns result or error.
 47	//
 48	// Receives result or error from Finalize step.
 49	Build *BuildStep
 50
 51	// Finalize performs final preparations needed before sending the message. The
 52	// message should already be complete by this stage, and is only alternated
 53	// to meet the expectations of the recipient (e.g. Retry and AWS SigV4
 54	// request signing)
 55	//
 56	// Takes Request, and returns result or error.
 57	//
 58	// Receives result or error from Deserialize step.
 59	Finalize *FinalizeStep
 60
 61	// Deserialize reacts to the handler's response returned by the recipient of the request
 62	// message. Deserializes the response into a structured type or error above
 63	// stacks can react to.
 64	//
 65	// Should only forward Request to underlying handler.
 66	//
 67	// Takes Request, and returns result or error.
 68	//
 69	// Receives raw response, or error from underlying handler.
 70	Deserialize *DeserializeStep
 71
 72	id string
 73}
 74
 75// NewStack returns an initialize empty stack.
 76func NewStack(id string, newRequestFn func() interface{}) *Stack {
 77	return &Stack{
 78		id:          id,
 79		Initialize:  NewInitializeStep(),
 80		Serialize:   NewSerializeStep(newRequestFn),
 81		Build:       NewBuildStep(),
 82		Finalize:    NewFinalizeStep(),
 83		Deserialize: NewDeserializeStep(),
 84	}
 85}
 86
 87// ID returns the unique ID for the stack as a middleware.
 88func (s *Stack) ID() string { return s.id }
 89
 90// HandleMiddleware invokes the middleware stack decorating the next handler.
 91// Each step of stack will be invoked in order before calling the next step.
 92// With the next handler call last.
 93//
 94// The input value must be the input parameters of the operation being
 95// performed.
 96//
 97// Will return the result of the operation, or error.
 98func (s *Stack) HandleMiddleware(ctx context.Context, input interface{}, next Handler) (
 99	output interface{}, metadata Metadata, err error,
100) {
101	h := DecorateHandler(next,
102		s.Initialize,
103		s.Serialize,
104		s.Build,
105		s.Finalize,
106		s.Deserialize,
107	)
108
109	return h.Handle(ctx, input)
110}
111
112// List returns a list of all middleware in the stack by step.
113func (s *Stack) List() []string {
114	var l []string
115	l = append(l, s.id)
116
117	l = append(l, s.Initialize.ID())
118	l = append(l, s.Initialize.List()...)
119
120	l = append(l, s.Serialize.ID())
121	l = append(l, s.Serialize.List()...)
122
123	l = append(l, s.Build.ID())
124	l = append(l, s.Build.List()...)
125
126	l = append(l, s.Finalize.ID())
127	l = append(l, s.Finalize.List()...)
128
129	l = append(l, s.Deserialize.ID())
130	l = append(l, s.Deserialize.List()...)
131
132	return l
133}
134
135func (s *Stack) String() string {
136	var b strings.Builder
137
138	w := &indentWriter{w: &b}
139
140	w.WriteLine(s.id)
141	w.Push()
142
143	writeStepItems(w, s.Initialize)
144	writeStepItems(w, s.Serialize)
145	writeStepItems(w, s.Build)
146	writeStepItems(w, s.Finalize)
147	writeStepItems(w, s.Deserialize)
148
149	return b.String()
150}
151
152type stackStepper interface {
153	ID() string
154	List() []string
155}
156
157func writeStepItems(w *indentWriter, s stackStepper) {
158	type lister interface {
159		List() []string
160	}
161
162	w.WriteLine(s.ID())
163	w.Push()
164
165	defer w.Pop()
166
167	// ignore stack to prevent circular iterations
168	if _, ok := s.(*Stack); ok {
169		return
170	}
171
172	for _, id := range s.List() {
173		w.WriteLine(id)
174	}
175}
176
177type stringWriter interface {
178	io.Writer
179	WriteString(string) (int, error)
180	WriteRune(rune) (int, error)
181}
182
183type indentWriter struct {
184	w     stringWriter
185	depth int
186}
187
188const indentDepth = "\t\t\t\t\t\t\t\t\t\t"
189
190func (w *indentWriter) Push() {
191	w.depth++
192}
193
194func (w *indentWriter) Pop() {
195	w.depth--
196	if w.depth < 0 {
197		w.depth = 0
198	}
199}
200
201func (w *indentWriter) WriteLine(v string) {
202	w.w.WriteString(indentDepth[:w.depth])
203
204	v = strings.ReplaceAll(v, "\n", "\\n")
205	v = strings.ReplaceAll(v, "\r", "\\r")
206
207	w.w.WriteString(v)
208	w.w.WriteRune('\n')
209}