-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
user_agent.rb
149 lines (126 loc) · 4.44 KB
/
user_agent.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# frozen_string_literal: true
module Aws
module Plugins
# @api private
class UserAgent < Seahorse::Client::Plugin
# @api private
option(:user_agent_suffix)
# @api private
option(:user_agent_frameworks, default: [])
option(
:sdk_ua_app_id,
doc_type: 'String',
docstring: <<-DOCS) do |cfg|
A unique and opaque application ID that is appended to the
User-Agent header as app/<sdk_ua_app_id>. It should have a
maximum length of 50.
DOCS
app_id = ENV['AWS_SDK_UA_APP_ID']
app_id ||= Aws.shared_config.sdk_ua_app_id(profile: cfg.profile)
app_id
end
def self.feature(feature, &block)
Thread.current[:aws_sdk_core_user_agent_feature] ||= []
Thread.current[:aws_sdk_core_user_agent_feature] << "ft/#{feature}"
block.call
ensure
Thread.current[:aws_sdk_core_user_agent_feature].pop
end
# @api private
class Handler < Seahorse::Client::Handler
def call(context)
set_user_agent(context)
@handler.call(context)
end
def set_user_agent(context)
context.http_request.headers['User-Agent'] = UserAgent.new(context).to_s
end
class UserAgent
def initialize(context)
@context = context
end
def to_s
ua = "aws-sdk-ruby3/#{CORE_GEM_VERSION}"
ua += ' ua/2.0'
ua += " #{api_metadata}" if api_metadata
ua += " #{os_metadata}"
ua += " #{language_metadata}"
ua += " #{env_metadata}" if env_metadata
ua += " #{config_metadata}" if config_metadata
ua += " #{app_id}" if app_id
ua += " #{feature_metadata}" if feature_metadata
ua += " #{framework_metadata}" if framework_metadata
if @context.config.user_agent_suffix
ua += " #{@context.config.user_agent_suffix}"
end
ua.strip
end
private
# Used to be gem_name/gem_version
def api_metadata
service_id = @context.config.api.metadata['serviceId']
return unless service_id
service_id = service_id.gsub(' ', '_').downcase
gem_version = @context[:gem_version]
"api/#{service_id}##{gem_version}"
end
# Used to be RUBY_PLATFORM
def os_metadata
os =
case RbConfig::CONFIG['host_os']
when /mac|darwin/
'macos'
when /linux|cygwin/
'linux'
when /mingw|mswin/
'windows'
else
'other'
end
metadata = "os/#{os}"
local_version = Gem::Platform.local.version
metadata += "##{local_version}" if local_version
metadata += " md/#{RbConfig::CONFIG['host_cpu']}"
metadata
end
# Used to be RUBY_ENGINE/RUBY_VERSION
def language_metadata
"lang/#{RUBY_ENGINE}##{RUBY_ENGINE_VERSION} md/#{RUBY_VERSION}"
end
def env_metadata
return unless (execution_env = ENV['AWS_EXECUTION_ENV'])
"exec-env/#{execution_env}"
end
def config_metadata
"cfg/retry-mode##{@context.config.retry_mode}"
end
def app_id
return unless (app_id = @context.config.sdk_ua_app_id)
# Sanitize and only allow these characters
app_id = app_id.gsub(/[^!#$%&'*+\-.^_`|~0-9A-Za-z]/, '-')
"app/#{app_id}"
end
def feature_metadata
return unless Thread.current[:aws_sdk_core_user_agent_feature]
Thread.current[:aws_sdk_core_user_agent_feature].join(' ')
end
def framework_metadata
if (frameworks_cfg = @context.config.user_agent_frameworks).empty?
return
end
# Frameworks may be aws-record, aws-sdk-rails, etc.
regex = /gems\/(?<name>#{frameworks_cfg.join('|')})-(?<version>\d+\.\d+\.\d+)/.freeze
frameworks = {}
Kernel.caller.each do |line|
match = line.match(regex)
next unless match
frameworks[match[:name]] = match[:version]
end
frameworks.map { |n, v| "lib/#{n}##{v}" }.join(' ')
end
end
end
handler(Handler, priority: 1)
end
end
end