stack.go raw

   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