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 }