...

Source file src/github.com/aws/smithy-go/middleware/stack.go

Documentation: github.com/aws/smithy-go/middleware

     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  

View as plain text