stack.go (5409B)
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 }