code.dwrz.net

Go monorepo.
Log | Files | Refs

user_agent.go (6932B)


      1 package middleware
      2 
      3 import (
      4 	"context"
      5 	"fmt"
      6 	"os"
      7 	"runtime"
      8 	"strings"
      9 
     10 	"github.com/aws/aws-sdk-go-v2/aws"
     11 	"github.com/aws/smithy-go/middleware"
     12 	smithyhttp "github.com/aws/smithy-go/transport/http"
     13 )
     14 
     15 var languageVersion = strings.TrimPrefix(runtime.Version(), "go")
     16 
     17 // SDKAgentKeyType is the metadata type to add to the SDK agent string
     18 type SDKAgentKeyType int
     19 
     20 // The set of valid SDKAgentKeyType constants. If an unknown value is assigned for SDKAgentKeyType it will
     21 // be mapped to AdditionalMetadata.
     22 const (
     23 	_ SDKAgentKeyType = iota
     24 	APIMetadata
     25 	OperatingSystemMetadata
     26 	LanguageMetadata
     27 	EnvironmentMetadata
     28 	FeatureMetadata
     29 	ConfigMetadata
     30 	FrameworkMetadata
     31 	AdditionalMetadata
     32 	ApplicationIdentifier
     33 )
     34 
     35 func (k SDKAgentKeyType) string() string {
     36 	switch k {
     37 	case APIMetadata:
     38 		return "api"
     39 	case OperatingSystemMetadata:
     40 		return "os"
     41 	case LanguageMetadata:
     42 		return "lang"
     43 	case EnvironmentMetadata:
     44 		return "exec-env"
     45 	case FeatureMetadata:
     46 		return "ft"
     47 	case ConfigMetadata:
     48 		return "cfg"
     49 	case FrameworkMetadata:
     50 		return "lib"
     51 	case ApplicationIdentifier:
     52 		return "app"
     53 	case AdditionalMetadata:
     54 		fallthrough
     55 	default:
     56 		return "md"
     57 	}
     58 }
     59 
     60 const execEnvVar = `AWS_EXECUTION_ENV`
     61 
     62 // requestUserAgent is a build middleware that set the User-Agent for the request.
     63 type requestUserAgent struct {
     64 	sdkAgent, userAgent *smithyhttp.UserAgentBuilder
     65 }
     66 
     67 // newRequestUserAgent returns a new requestUserAgent which will set the User-Agent and X-Amz-User-Agent for the
     68 // request.
     69 //
     70 // User-Agent example:
     71 //
     72 //	aws-sdk-go-v2/1.2.3
     73 //
     74 // X-Amz-User-Agent example:
     75 //
     76 //	aws-sdk-go-v2/1.2.3 md/GOOS/linux md/GOARCH/amd64 lang/go/1.15
     77 func newRequestUserAgent() *requestUserAgent {
     78 	userAgent, sdkAgent := smithyhttp.NewUserAgentBuilder(), smithyhttp.NewUserAgentBuilder()
     79 	addProductName(userAgent)
     80 	addProductName(sdkAgent)
     81 
     82 	r := &requestUserAgent{
     83 		sdkAgent:  sdkAgent,
     84 		userAgent: userAgent,
     85 	}
     86 
     87 	addSDKMetadata(r)
     88 
     89 	return r
     90 }
     91 
     92 func addSDKMetadata(r *requestUserAgent) {
     93 	r.AddSDKAgentKey(OperatingSystemMetadata, getNormalizedOSName())
     94 	r.AddSDKAgentKeyValue(LanguageMetadata, "go", languageVersion)
     95 	r.AddSDKAgentKeyValue(AdditionalMetadata, "GOOS", runtime.GOOS)
     96 	r.AddSDKAgentKeyValue(AdditionalMetadata, "GOARCH", runtime.GOARCH)
     97 	if ev := os.Getenv(execEnvVar); len(ev) > 0 {
     98 		r.AddSDKAgentKey(EnvironmentMetadata, ev)
     99 	}
    100 }
    101 
    102 func addProductName(builder *smithyhttp.UserAgentBuilder) {
    103 	builder.AddKeyValue(aws.SDKName, aws.SDKVersion)
    104 }
    105 
    106 // AddUserAgentKey retrieves a requestUserAgent from the provided stack, or initializes one.
    107 func AddUserAgentKey(key string) func(*middleware.Stack) error {
    108 	return func(stack *middleware.Stack) error {
    109 		requestUserAgent, err := getOrAddRequestUserAgent(stack)
    110 		if err != nil {
    111 			return err
    112 		}
    113 		requestUserAgent.AddUserAgentKey(key)
    114 		return nil
    115 	}
    116 }
    117 
    118 // AddUserAgentKeyValue retrieves a requestUserAgent from the provided stack, or initializes one.
    119 func AddUserAgentKeyValue(key, value string) func(*middleware.Stack) error {
    120 	return func(stack *middleware.Stack) error {
    121 		requestUserAgent, err := getOrAddRequestUserAgent(stack)
    122 		if err != nil {
    123 			return err
    124 		}
    125 		requestUserAgent.AddUserAgentKeyValue(key, value)
    126 		return nil
    127 	}
    128 }
    129 
    130 // AddSDKAgentKey retrieves a requestUserAgent from the provided stack, or initializes one.
    131 func AddSDKAgentKey(keyType SDKAgentKeyType, key string) func(*middleware.Stack) error {
    132 	return func(stack *middleware.Stack) error {
    133 		requestUserAgent, err := getOrAddRequestUserAgent(stack)
    134 		if err != nil {
    135 			return err
    136 		}
    137 		requestUserAgent.AddSDKAgentKey(keyType, key)
    138 		return nil
    139 	}
    140 }
    141 
    142 // AddSDKAgentKeyValue retrieves a requestUserAgent from the provided stack, or initializes one.
    143 func AddSDKAgentKeyValue(keyType SDKAgentKeyType, key, value string) func(*middleware.Stack) error {
    144 	return func(stack *middleware.Stack) error {
    145 		requestUserAgent, err := getOrAddRequestUserAgent(stack)
    146 		if err != nil {
    147 			return err
    148 		}
    149 		requestUserAgent.AddSDKAgentKeyValue(keyType, key, value)
    150 		return nil
    151 	}
    152 }
    153 
    154 // AddRequestUserAgentMiddleware registers a requestUserAgent middleware on the stack if not present.
    155 func AddRequestUserAgentMiddleware(stack *middleware.Stack) error {
    156 	_, err := getOrAddRequestUserAgent(stack)
    157 	return err
    158 }
    159 
    160 func getOrAddRequestUserAgent(stack *middleware.Stack) (*requestUserAgent, error) {
    161 	id := (*requestUserAgent)(nil).ID()
    162 	bm, ok := stack.Build.Get(id)
    163 	if !ok {
    164 		bm = newRequestUserAgent()
    165 		err := stack.Build.Add(bm, middleware.After)
    166 		if err != nil {
    167 			return nil, err
    168 		}
    169 	}
    170 
    171 	requestUserAgent, ok := bm.(*requestUserAgent)
    172 	if !ok {
    173 		return nil, fmt.Errorf("%T for %s middleware did not match expected type", bm, id)
    174 	}
    175 
    176 	return requestUserAgent, nil
    177 }
    178 
    179 // AddUserAgentKey adds the component identified by name to the User-Agent string.
    180 func (u *requestUserAgent) AddUserAgentKey(key string) {
    181 	u.userAgent.AddKey(key)
    182 }
    183 
    184 // AddUserAgentKeyValue adds the key identified by the given name and value to the User-Agent string.
    185 func (u *requestUserAgent) AddUserAgentKeyValue(key, value string) {
    186 	u.userAgent.AddKeyValue(key, value)
    187 }
    188 
    189 // AddUserAgentKey adds the component identified by name to the User-Agent string.
    190 func (u *requestUserAgent) AddSDKAgentKey(keyType SDKAgentKeyType, key string) {
    191 	// TODO: should target sdkAgent
    192 	u.userAgent.AddKey(keyType.string() + "/" + key)
    193 }
    194 
    195 // AddUserAgentKeyValue adds the key identified by the given name and value to the User-Agent string.
    196 func (u *requestUserAgent) AddSDKAgentKeyValue(keyType SDKAgentKeyType, key, value string) {
    197 	// TODO: should target sdkAgent
    198 	u.userAgent.AddKeyValue(keyType.string()+"/"+key, value)
    199 }
    200 
    201 // ID the name of the middleware.
    202 func (u *requestUserAgent) ID() string {
    203 	return "UserAgent"
    204 }
    205 
    206 // HandleBuild adds or appends the constructed user agent to the request.
    207 func (u *requestUserAgent) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (
    208 	out middleware.BuildOutput, metadata middleware.Metadata, err error,
    209 ) {
    210 	switch req := in.Request.(type) {
    211 	case *smithyhttp.Request:
    212 		u.addHTTPUserAgent(req)
    213 		// TODO: To be re-enabled
    214 		// u.addHTTPSDKAgent(req)
    215 	default:
    216 		return out, metadata, fmt.Errorf("unknown transport type %T", in)
    217 	}
    218 
    219 	return next.HandleBuild(ctx, in)
    220 }
    221 
    222 func (u *requestUserAgent) addHTTPUserAgent(request *smithyhttp.Request) {
    223 	const userAgent = "User-Agent"
    224 	updateHTTPHeader(request, userAgent, u.userAgent.Build())
    225 }
    226 
    227 func (u *requestUserAgent) addHTTPSDKAgent(request *smithyhttp.Request) {
    228 	const sdkAgent = "X-Amz-User-Agent"
    229 	updateHTTPHeader(request, sdkAgent, u.sdkAgent.Build())
    230 }
    231 
    232 func updateHTTPHeader(request *smithyhttp.Request, header string, value string) {
    233 	var current string
    234 	if v := request.Header[header]; len(v) > 0 {
    235 		current = v[0]
    236 	}
    237 	if len(current) > 0 {
    238 		current = value + " " + current
    239 	} else {
    240 		current = value
    241 	}
    242 	request.Header[header] = append(request.Header[header][:0], current)
    243 }