interceptor.go raw

   1  package http
   2  
   3  import (
   4  	"context"
   5  )
   6  
   7  func icopy[T any](v []T) []T {
   8  	s := make([]T, len(v))
   9  	copy(s, v)
  10  	return s
  11  }
  12  
  13  // InterceptorContext is all the information available in different
  14  // interceptors.
  15  //
  16  // Not all information is available in each interceptor, see each interface
  17  // definition for more details.
  18  type InterceptorContext struct {
  19  	Input   any
  20  	Request *Request
  21  
  22  	Output   any
  23  	Response *Response
  24  }
  25  
  26  // InterceptorRegistry holds a list of operation interceptors.
  27  //
  28  // Interceptors allow callers to insert custom behavior at well-defined points
  29  // within a client's operation lifecycle.
  30  //
  31  // # Interceptor context
  32  //
  33  // All interceptors are invoked with a context object that contains input and
  34  // output containers for the operation. The individual fields that are
  35  // available will depend on what the interceptor is and, in certain
  36  // interceptors, how far the operation was able to progress. See the
  37  // documentation for each interface definition for more information about field
  38  // availability.
  39  //
  40  // Implementations MUST NOT directly mutate the values of the fields in the
  41  // interceptor context. They are free to mutate the existing values _pointed
  42  // to_ by those fields, however.
  43  //
  44  // # Returning errors
  45  //
  46  // All interceptors can return errors. If an interceptor returns an error
  47  // _before_ the client's retry loop, the operation will fail immediately. If
  48  // one returns an error _within_ the retry loop, the error WILL be considered
  49  // according to the client's retry policy.
  50  //
  51  // # Adding interceptors
  52  //
  53  // Idiomatically you will simply use one of the Add() receiver methods to
  54  // register interceptors as desired. However, the list for each interface is
  55  // exported on the registry struct and the caller is free to manipulate it
  56  // directly, for example, to register a number of interceptors all at once, or
  57  // to remove one that was previously registered.
  58  //
  59  // The base SDK client WILL NOT add any interceptors. SDK operations and
  60  // customizations are implemented in terms of middleware.
  61  //
  62  // Modifications to the registry will not persist across operation calls when
  63  // using per-operation functional options. This means you can register
  64  // interceptors on a per-operation basis without affecting other operations.
  65  type InterceptorRegistry struct {
  66  	BeforeExecution       []BeforeExecutionInterceptor
  67  	BeforeSerialization   []BeforeSerializationInterceptor
  68  	AfterSerialization    []AfterSerializationInterceptor
  69  	BeforeRetryLoop       []BeforeRetryLoopInterceptor
  70  	BeforeAttempt         []BeforeAttemptInterceptor
  71  	BeforeSigning         []BeforeSigningInterceptor
  72  	AfterSigning          []AfterSigningInterceptor
  73  	BeforeTransmit        []BeforeTransmitInterceptor
  74  	AfterTransmit         []AfterTransmitInterceptor
  75  	BeforeDeserialization []BeforeDeserializationInterceptor
  76  	AfterDeserialization  []AfterDeserializationInterceptor
  77  	AfterAttempt          []AfterAttemptInterceptor
  78  	AfterExecution        []AfterExecutionInterceptor
  79  }
  80  
  81  // Copy returns a deep copy of the registry. This is used by SDK clients on
  82  // each operation call in order to prevent per-op config mutation from
  83  // persisting.
  84  func (i *InterceptorRegistry) Copy() InterceptorRegistry {
  85  	return InterceptorRegistry{
  86  		BeforeExecution:       icopy(i.BeforeExecution),
  87  		BeforeSerialization:   icopy(i.BeforeSerialization),
  88  		AfterSerialization:    icopy(i.AfterSerialization),
  89  		BeforeRetryLoop:       icopy(i.BeforeRetryLoop),
  90  		BeforeAttempt:         icopy(i.BeforeAttempt),
  91  		BeforeSigning:         icopy(i.BeforeSigning),
  92  		AfterSigning:          icopy(i.AfterSigning),
  93  		BeforeTransmit:        icopy(i.BeforeTransmit),
  94  		AfterTransmit:         icopy(i.AfterTransmit),
  95  		BeforeDeserialization: icopy(i.BeforeDeserialization),
  96  		AfterDeserialization:  icopy(i.AfterDeserialization),
  97  		AfterAttempt:          icopy(i.AfterAttempt),
  98  		AfterExecution:        icopy(i.AfterExecution),
  99  	}
 100  }
 101  
 102  // AddBeforeExecution registers the provided BeforeExecutionInterceptor.
 103  func (i *InterceptorRegistry) AddBeforeExecution(v BeforeExecutionInterceptor) {
 104  	i.BeforeExecution = append(i.BeforeExecution, v)
 105  }
 106  
 107  // AddBeforeSerialization registers the provided BeforeSerializationInterceptor.
 108  func (i *InterceptorRegistry) AddBeforeSerialization(v BeforeSerializationInterceptor) {
 109  	i.BeforeSerialization = append(i.BeforeSerialization, v)
 110  }
 111  
 112  // AddAfterSerialization registers the provided AfterSerializationInterceptor.
 113  func (i *InterceptorRegistry) AddAfterSerialization(v AfterSerializationInterceptor) {
 114  	i.AfterSerialization = append(i.AfterSerialization, v)
 115  }
 116  
 117  // AddBeforeRetryLoop registers the provided BeforeRetryLoopInterceptor.
 118  func (i *InterceptorRegistry) AddBeforeRetryLoop(v BeforeRetryLoopInterceptor) {
 119  	i.BeforeRetryLoop = append(i.BeforeRetryLoop, v)
 120  }
 121  
 122  // AddBeforeAttempt registers the provided BeforeAttemptInterceptor.
 123  func (i *InterceptorRegistry) AddBeforeAttempt(v BeforeAttemptInterceptor) {
 124  	i.BeforeAttempt = append(i.BeforeAttempt, v)
 125  }
 126  
 127  // AddBeforeSigning registers the provided BeforeSigningInterceptor.
 128  func (i *InterceptorRegistry) AddBeforeSigning(v BeforeSigningInterceptor) {
 129  	i.BeforeSigning = append(i.BeforeSigning, v)
 130  }
 131  
 132  // AddAfterSigning registers the provided AfterSigningInterceptor.
 133  func (i *InterceptorRegistry) AddAfterSigning(v AfterSigningInterceptor) {
 134  	i.AfterSigning = append(i.AfterSigning, v)
 135  }
 136  
 137  // AddBeforeTransmit registers the provided BeforeTransmitInterceptor.
 138  func (i *InterceptorRegistry) AddBeforeTransmit(v BeforeTransmitInterceptor) {
 139  	i.BeforeTransmit = append(i.BeforeTransmit, v)
 140  }
 141  
 142  // AddAfterTransmit registers the provided AfterTransmitInterceptor.
 143  func (i *InterceptorRegistry) AddAfterTransmit(v AfterTransmitInterceptor) {
 144  	i.AfterTransmit = append(i.AfterTransmit, v)
 145  }
 146  
 147  // AddBeforeDeserialization registers the provided BeforeDeserializationInterceptor.
 148  func (i *InterceptorRegistry) AddBeforeDeserialization(v BeforeDeserializationInterceptor) {
 149  	i.BeforeDeserialization = append(i.BeforeDeserialization, v)
 150  }
 151  
 152  // AddAfterDeserialization registers the provided AfterDeserializationInterceptor.
 153  func (i *InterceptorRegistry) AddAfterDeserialization(v AfterDeserializationInterceptor) {
 154  	i.AfterDeserialization = append(i.AfterDeserialization, v)
 155  }
 156  
 157  // AddAfterAttempt registers the provided AfterAttemptInterceptor.
 158  func (i *InterceptorRegistry) AddAfterAttempt(v AfterAttemptInterceptor) {
 159  	i.AfterAttempt = append(i.AfterAttempt, v)
 160  }
 161  
 162  // AddAfterExecution registers the provided AfterExecutionInterceptor.
 163  func (i *InterceptorRegistry) AddAfterExecution(v AfterExecutionInterceptor) {
 164  	i.AfterExecution = append(i.AfterExecution, v)
 165  }
 166  
 167  // BeforeExecutionInterceptor runs before anything else in the operation
 168  // lifecycle.
 169  //
 170  // Available InterceptorContext fields:
 171  //   - Input
 172  type BeforeExecutionInterceptor interface {
 173  	BeforeExecution(ctx context.Context, in *InterceptorContext) error
 174  }
 175  
 176  // BeforeSerializationInterceptor runs before the operation input is serialized
 177  // into its transport request.
 178  //
 179  // Serialization occurs before the operation's retry loop.
 180  //
 181  // Available InterceptorContext fields:
 182  //   - Input
 183  type BeforeSerializationInterceptor interface {
 184  	BeforeSerialization(ctx context.Context, in *InterceptorContext) error
 185  }
 186  
 187  // AfterSerializationInterceptor runs after the operation input is serialized
 188  // into its transport request.
 189  //
 190  // Available InterceptorContext fields:
 191  //   - Input
 192  //   - Request
 193  type AfterSerializationInterceptor interface {
 194  	AfterSerialization(ctx context.Context, in *InterceptorContext) error
 195  }
 196  
 197  // BeforeRetryLoopInterceptor runs right before the operation enters the retry loop.
 198  //
 199  // Available InterceptorContext fields:
 200  //   - Input
 201  //   - Request
 202  type BeforeRetryLoopInterceptor interface {
 203  	BeforeRetryLoop(ctx context.Context, in *InterceptorContext) error
 204  }
 205  
 206  // BeforeAttemptInterceptor runs right before every attempt in the retry loop.
 207  //
 208  // If this interceptor returns an error, AfterAttempt interceptors WILL NOT be
 209  // invoked.
 210  //
 211  // Available InterceptorContext fields:
 212  //   - Input
 213  //   - Request
 214  type BeforeAttemptInterceptor interface {
 215  	BeforeAttempt(ctx context.Context, in *InterceptorContext) error
 216  }
 217  
 218  // BeforeSigningInterceptor runs right before the request is signed.
 219  //
 220  // Signing occurs within the operation's retry loop.
 221  //
 222  // Available InterceptorContext fields:
 223  //   - Input
 224  //   - Request
 225  type BeforeSigningInterceptor interface {
 226  	BeforeSigning(ctx context.Context, in *InterceptorContext) error
 227  }
 228  
 229  // AfterSigningInterceptor runs right after the request is signed.
 230  //
 231  // It is unsafe to modify the outgoing HTTP request at or past this hook, since
 232  // doing so may invalidate the signature of the request.
 233  //
 234  // Available InterceptorContext fields:
 235  //   - Input
 236  //   - Request
 237  type AfterSigningInterceptor interface {
 238  	AfterSigning(ctx context.Context, in *InterceptorContext) error
 239  }
 240  
 241  // BeforeTransmitInterceptor runs right before the HTTP request is sent.
 242  //
 243  // HTTP transmit occurs within the operation's retry loop.
 244  //
 245  // Available InterceptorContext fields:
 246  //   - Input
 247  //   - Request
 248  type BeforeTransmitInterceptor interface {
 249  	BeforeTransmit(ctx context.Context, in *InterceptorContext) error
 250  }
 251  
 252  // AfterTransmitInterceptor runs right after the HTTP response is received.
 253  //
 254  // It will always be invoked when a response is received, regardless of its
 255  // status code. Conversely, it WILL NOT be invoked if the HTTP round-trip was
 256  // not successful, e.g. because of a DNS resolution error
 257  //
 258  // Available InterceptorContext fields:
 259  //   - Input
 260  //   - Request
 261  //   - Response
 262  type AfterTransmitInterceptor interface {
 263  	AfterTransmit(ctx context.Context, in *InterceptorContext) error
 264  }
 265  
 266  // BeforeDeserializationInterceptor runs right before the incoming HTTP response
 267  // is deserialized.
 268  //
 269  // This interceptor IS NOT invoked if the HTTP round-trip was not successful.
 270  //
 271  // Deserialization occurs within the operation's retry loop.
 272  //
 273  // Available InterceptorContext fields:
 274  //   - Input
 275  //   - Request
 276  //   - Response
 277  type BeforeDeserializationInterceptor interface {
 278  	BeforeDeserialization(ctx context.Context, in *InterceptorContext) error
 279  }
 280  
 281  // AfterDeserializationInterceptor runs right after the incoming HTTP response
 282  // is deserialized. This hook is invoked regardless of whether the deserialized
 283  // result was an error.
 284  //
 285  // This interceptor IS NOT invoked if the HTTP round-trip was not successful.
 286  //
 287  // Available InterceptorContext fields:
 288  //   - Input
 289  //   - Output (IF the operation had a success-level response)
 290  //   - Request
 291  //   - Response
 292  type AfterDeserializationInterceptor interface {
 293  	AfterDeserialization(ctx context.Context, in *InterceptorContext) error
 294  }
 295  
 296  // AfterAttemptInterceptor runs right after the incoming HTTP response
 297  // is deserialized. This hook is invoked regardless of whether the deserialized
 298  // result was an error, or if another interceptor within the retry loop
 299  // returned an error.
 300  //
 301  // Available InterceptorContext fields:
 302  //   - Input
 303  //   - Output (IF the operation had a success-level response)
 304  //   - Request (IF the operation did not return an error during serialization)
 305  //   - Response (IF the operation was able to transmit the HTTP request)
 306  type AfterAttemptInterceptor interface {
 307  	AfterAttempt(ctx context.Context, in *InterceptorContext) error
 308  }
 309  
 310  // AfterExecutionInterceptor runs after everything else. It runs regardless of
 311  // how far the operation progressed in its lifecycle, and regardless of whether
 312  // the operation succeeded or failed.
 313  //
 314  // Available InterceptorContext fields:
 315  //   - Input
 316  //   - Output (IF the operation had a success-level response)
 317  //   - Request (IF the operation did not return an error during serialization)
 318  //   - Response (IF the operation was able to transmit the HTTP request)
 319  type AfterExecutionInterceptor interface {
 320  	AfterExecution(ctx context.Context, in *InterceptorContext) error
 321  }
 322