src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

middleware.go (5383B)


      1 package middleware
      2 
      3 import (
      4 	"context"
      5 	"fmt"
      6 	"time"
      7 
      8 	"github.com/aws/aws-sdk-go-v2/internal/rand"
      9 	"github.com/aws/aws-sdk-go-v2/internal/sdk"
     10 	"github.com/aws/smithy-go/logging"
     11 	"github.com/aws/smithy-go/middleware"
     12 	smithyrand "github.com/aws/smithy-go/rand"
     13 	smithyhttp "github.com/aws/smithy-go/transport/http"
     14 )
     15 
     16 // ClientRequestID is a Smithy BuildMiddleware that will generate a unique ID for logical API operation
     17 // invocation.
     18 type ClientRequestID struct{}
     19 
     20 // ID the identifier for the ClientRequestID
     21 func (r *ClientRequestID) ID() string {
     22 	return "ClientRequestID"
     23 }
     24 
     25 // HandleBuild attaches a unique operation invocation id for the operation to the request
     26 func (r ClientRequestID) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (
     27 	out middleware.BuildOutput, metadata middleware.Metadata, err error,
     28 ) {
     29 	req, ok := in.Request.(*smithyhttp.Request)
     30 	if !ok {
     31 		return out, metadata, fmt.Errorf("unknown transport type %T", req)
     32 	}
     33 
     34 	invocationID, err := smithyrand.NewUUID(rand.Reader).GetUUID()
     35 	if err != nil {
     36 		return out, metadata, err
     37 	}
     38 
     39 	const invocationIDHeader = "Amz-Sdk-Invocation-Id"
     40 	req.Header[invocationIDHeader] = append(req.Header[invocationIDHeader][:0], invocationID)
     41 
     42 	return next.HandleBuild(ctx, in)
     43 }
     44 
     45 // RecordResponseTiming records the response timing for the SDK client requests.
     46 type RecordResponseTiming struct{}
     47 
     48 // ID is the middleware identifier
     49 func (a *RecordResponseTiming) ID() string {
     50 	return "RecordResponseTiming"
     51 }
     52 
     53 // HandleDeserialize calculates response metadata and clock skew
     54 func (a RecordResponseTiming) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
     55 	out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
     56 ) {
     57 	out, metadata, err = next.HandleDeserialize(ctx, in)
     58 	responseAt := sdk.NowTime()
     59 	setResponseAt(&metadata, responseAt)
     60 
     61 	var serverTime time.Time
     62 
     63 	switch resp := out.RawResponse.(type) {
     64 	case *smithyhttp.Response:
     65 		respDateHeader := resp.Header.Get("Date")
     66 		if len(respDateHeader) == 0 {
     67 			break
     68 		}
     69 		var parseErr error
     70 		serverTime, parseErr = smithyhttp.ParseTime(respDateHeader)
     71 		if parseErr != nil {
     72 			logger := middleware.GetLogger(ctx)
     73 			logger.Logf(logging.Warn, "failed to parse response Date header value, got %v",
     74 				parseErr.Error())
     75 			break
     76 		}
     77 		setServerTime(&metadata, serverTime)
     78 	}
     79 
     80 	if !serverTime.IsZero() {
     81 		attemptSkew := serverTime.Sub(responseAt)
     82 		setAttemptSkew(&metadata, attemptSkew)
     83 	}
     84 
     85 	return out, metadata, err
     86 }
     87 
     88 type responseAtKey struct{}
     89 
     90 // GetResponseAt returns the time response was received at.
     91 func GetResponseAt(metadata middleware.Metadata) (v time.Time, ok bool) {
     92 	v, ok = metadata.Get(responseAtKey{}).(time.Time)
     93 	return v, ok
     94 }
     95 
     96 // setResponseAt sets the response time on the metadata.
     97 func setResponseAt(metadata *middleware.Metadata, v time.Time) {
     98 	metadata.Set(responseAtKey{}, v)
     99 }
    100 
    101 type serverTimeKey struct{}
    102 
    103 // GetServerTime returns the server time for response.
    104 func GetServerTime(metadata middleware.Metadata) (v time.Time, ok bool) {
    105 	v, ok = metadata.Get(serverTimeKey{}).(time.Time)
    106 	return v, ok
    107 }
    108 
    109 // setServerTime sets the server time on the metadata.
    110 func setServerTime(metadata *middleware.Metadata, v time.Time) {
    111 	metadata.Set(serverTimeKey{}, v)
    112 }
    113 
    114 type attemptSkewKey struct{}
    115 
    116 // GetAttemptSkew returns Attempt clock skew for response from metadata.
    117 func GetAttemptSkew(metadata middleware.Metadata) (v time.Duration, ok bool) {
    118 	v, ok = metadata.Get(attemptSkewKey{}).(time.Duration)
    119 	return v, ok
    120 }
    121 
    122 // setAttemptSkew sets the attempt clock skew on the metadata.
    123 func setAttemptSkew(metadata *middleware.Metadata, v time.Duration) {
    124 	metadata.Set(attemptSkewKey{}, v)
    125 }
    126 
    127 // AddClientRequestIDMiddleware adds ClientRequestID to the middleware stack
    128 func AddClientRequestIDMiddleware(stack *middleware.Stack) error {
    129 	return stack.Build.Add(&ClientRequestID{}, middleware.After)
    130 }
    131 
    132 // AddRecordResponseTiming adds RecordResponseTiming middleware to the
    133 // middleware stack.
    134 func AddRecordResponseTiming(stack *middleware.Stack) error {
    135 	return stack.Deserialize.Add(&RecordResponseTiming{}, middleware.After)
    136 }
    137 
    138 // rawResponseKey is the accessor key used to store and access the
    139 // raw response within the response metadata.
    140 type rawResponseKey struct{}
    141 
    142 // AddRawResponse middleware adds raw response on to the metadata
    143 type AddRawResponse struct{}
    144 
    145 // ID the identifier for the ClientRequestID
    146 func (m *AddRawResponse) ID() string {
    147 	return "AddRawResponseToMetadata"
    148 }
    149 
    150 // HandleDeserialize adds raw response on the middleware metadata
    151 func (m AddRawResponse) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
    152 	out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
    153 ) {
    154 	out, metadata, err = next.HandleDeserialize(ctx, in)
    155 	metadata.Set(rawResponseKey{}, out.RawResponse)
    156 	return out, metadata, err
    157 }
    158 
    159 // AddRawResponseToMetadata adds middleware to the middleware stack that
    160 // store raw response on to the metadata.
    161 func AddRawResponseToMetadata(stack *middleware.Stack) error {
    162 	return stack.Deserialize.Add(&AddRawResponse{}, middleware.Before)
    163 }
    164 
    165 // GetRawResponse returns raw response set on metadata
    166 func GetRawResponse(metadata middleware.Metadata) interface{} {
    167 	return metadata.Get(rawResponseKey{})
    168 }