1 package middleware
2 3 import (
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
25 type 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.
76 func 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.
88 func (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.
98 func (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.
113 func (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 135 func (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 152 type stackStepper interface {
153 ID() string
154 List() []string
155 }
156 157 func 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 177 type stringWriter interface {
178 io.Writer
179 WriteString(string) (int, error)
180 WriteRune(rune) (int, error)
181 }
182 183 type indentWriter struct {
184 w stringWriter
185 depth int
186 }
187 188 const indentDepth = "\t\t\t\t\t\t\t\t\t\t"
189 190 func (w *indentWriter) Push() {
191 w.depth++
192 }
193 194 func (w *indentWriter) Pop() {
195 w.depth--
196 if w.depth < 0 {
197 w.depth = 0
198 }
199 }
200 201 func (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 }
210