Skip to content

User Agent #310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jul 11, 2025
68 changes: 68 additions & 0 deletions gems/smithy-client/lib/smithy-client/plugins/user_agent.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

module Smithy
module Client
module Plugins
# @api private
class UserAgent < Plugin
option(
:user_agent_suffix,
doc_type: String,
docstring: <<~DOCS)
An optional string that is appended to the User-Agent header.
The default User-Agent includes the smithy-client version,
the ruby platform and version, and host OS information.
DOCS

# @api private
class Handler < Client::Handler
def call(context)
context.http_request.headers['User-Agent'] = UserAgent.new(context).to_s
@handler.call(context)
end

# @api private
class UserAgent
def initialize(context)
@context = context
end

def to_s
ua = "smithy-ruby/#{Smithy::Client::VERSION}"
ua += " (#{os_metadata};"
ua += " #{language_metadata})"
ua += " #{@context.config.user_agent_suffix}" if @context.config.user_agent_suffix
ua.strip
end

private

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.to_s
local_version = Gem::Platform.local.version
metadata += " #{local_version}" if local_version
metadata + "; #{RbConfig::CONFIG['host_cpu']}"
end

def language_metadata
"ruby/#{RUBY_VERSION}"
end
end
end

handler(Handler, step: :sign, priority: 5)
end
end
end
end
30 changes: 30 additions & 0 deletions gems/smithy-client/spec/smithy-client/plugins/user_agent_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require_relative '../../spec_helper'

module Smithy
module Client
module Plugins
describe UserAgent do
let(:client_class) { ClientHelper.sample_client.const_get(:Client) }
let(:client) { client_class.new(stub_responses: true) }

it 'adds a user agent header to request' do
resp = client.operation
ua_header = resp.context.http_request.headers['user-agent']
expect(ua_header).to_not be_nil
expect(ua_header).to include('smithy-ruby')
expect(ua_header).to include('macos').or include('linux').or include('windows').or include('other')
expect(ua_header).to include('ruby')
end

it 'adds a user agent suffix to user agent string when configured' do
client = client_class.new(user_agent_suffix: 'test-suffix', stub_responses: true)
resp = client.operation
ua_header = resp.context.http_request.headers['user-agent']
expect(ua_header).to end_with('test-suffix')
end
end
end
end
end
6 changes: 4 additions & 2 deletions gems/smithy/lib/smithy/welds/plugins.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require 'smithy-client/plugins/response_target'
require 'smithy-client/plugins/retry_errors'
require 'smithy-client/plugins/stub_responses'
require 'smithy-client/plugins/user_agent'

module Smithy
module Welds
Expand All @@ -27,7 +28,7 @@ def for?(_service)
true
end

def add_plugins
def add_plugins # rubocop:disable Metrics/MethodLength
base_path = 'smithy-client/plugins'
{
Smithy::Client::Plugins::ChecksumRequired => { require_path: "#{base_path}/checksum_required" },
Expand All @@ -46,7 +47,8 @@ def add_plugins
Smithy::Client::Plugins::ResolveAuth => { require_path: "#{base_path}/resolve_auth" },
Smithy::Client::Plugins::ResponseTarget => { require_path: "#{base_path}/response_target" },
Smithy::Client::Plugins::RetryErrors => { require_path: "#{base_path}/retry_errors" },
Smithy::Client::Plugins::StubResponses => { require_path: "#{base_path}/stub_responses" }
Smithy::Client::Plugins::StubResponses => { require_path: "#{base_path}/stub_responses" },
Smithy::Client::Plugins::UserAgent => { require_path: "#{base_path}/user_agent" }
}
end
end
Expand Down
6 changes: 6 additions & 0 deletions projections/shapes/lib/shapes/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
require 'smithy-client/plugins/response_target'
require 'smithy-client/plugins/retry_errors'
require 'smithy-client/plugins/stub_responses'
require 'smithy-client/plugins/user_agent'

module ShapeService
# An API client for ShapeService.
Expand Down Expand Up @@ -47,6 +48,7 @@ class Client < Smithy::Client::Base
add_plugin(Smithy::Client::Plugins::ResponseTarget)
add_plugin(Smithy::Client::Plugins::RetryErrors)
add_plugin(Smithy::Client::Plugins::StubResponses)
add_plugin(Smithy::Client::Plugins::UserAgent)

# @param options [Hash] Client options
# @option options [Boolean] :adaptive_retry_wait_to_fill (true)
Expand Down Expand Up @@ -158,6 +160,10 @@ class Client < Smithy::Client::Base
# By default fake responses are generated and returned. You can specify the response data
# to return or errors to raise by calling {Stubs#stub_responses}.
# @see Stubs
# @option options [String] :user_agent_suffix
# An optional string that is appended to the User-Agent header.
# The default User-Agent includes the smithy-client version,
# the ruby platform and version, and host OS information.
# @option options [Boolean] :validate_params (true)
# When `true`, request parameters are validated before sending the request.
def initialize(*options)
Expand Down
1 change: 1 addition & 0 deletions projections/shapes/sig/shapes/client.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module ShapeService
?retry_max_attempts: Integer,
?retry_strategy: String | Class,
?stub_responses: bool,
?user_agent_suffix: String,
?validate_params: bool,
) -> void | (?Hash[Symbol, untyped]) -> void

Expand Down
6 changes: 6 additions & 0 deletions projections/weather/lib/weather/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
require 'smithy-client/plugins/response_target'
require 'smithy-client/plugins/retry_errors'
require 'smithy-client/plugins/stub_responses'
require 'smithy-client/plugins/user_agent'

module Weather
# An API client for Weather.
Expand Down Expand Up @@ -47,6 +48,7 @@ class Client < Smithy::Client::Base
add_plugin(Smithy::Client::Plugins::ResponseTarget)
add_plugin(Smithy::Client::Plugins::RetryErrors)
add_plugin(Smithy::Client::Plugins::StubResponses)
add_plugin(Smithy::Client::Plugins::UserAgent)

# @param options [Hash] Client options
# @option options [Boolean] :adaptive_retry_wait_to_fill (true)
Expand Down Expand Up @@ -158,6 +160,10 @@ class Client < Smithy::Client::Base
# By default fake responses are generated and returned. You can specify the response data
# to return or errors to raise by calling {Stubs#stub_responses}.
# @see Stubs
# @option options [String] :user_agent_suffix
# An optional string that is appended to the User-Agent header.
# The default User-Agent includes the smithy-client version,
# the ruby platform and version, and host OS information.
# @option options [Boolean] :validate_params (true)
# When `true`, request parameters are validated before sending the request.
def initialize(*options)
Expand Down
1 change: 1 addition & 0 deletions projections/weather/sig/weather/client.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module Weather
?retry_max_attempts: Integer,
?retry_strategy: String | Class,
?stub_responses: bool,
?user_agent_suffix: String,
?validate_params: bool,
) -> void | (?Hash[Symbol, untyped]) -> void

Expand Down