smithy-client 1.0.0.pre0 → 1.0.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -0
  3. data/VERSION +1 -1
  4. data/lib/smithy-client/anonymous_provider.rb +12 -0
  5. data/lib/smithy-client/auth_option.rb +23 -0
  6. data/lib/smithy-client/auth_scheme.rb +25 -0
  7. data/lib/smithy-client/auth_schemes/anonymous.rb +18 -0
  8. data/lib/smithy-client/auth_schemes/http_api_key.rb +18 -0
  9. data/lib/smithy-client/auth_schemes/http_basic.rb +18 -0
  10. data/lib/smithy-client/auth_schemes/http_bearer.rb +18 -0
  11. data/lib/smithy-client/auth_schemes/http_digest.rb +18 -0
  12. data/lib/smithy-client/base.rb +200 -0
  13. data/lib/smithy-client/block_io.rb +36 -0
  14. data/lib/smithy-client/configuration.rb +222 -0
  15. data/lib/smithy-client/default_params.rb +91 -0
  16. data/lib/smithy-client/dynamic_errors.rb +82 -0
  17. data/lib/smithy-client/endpoint_rules.rb +186 -0
  18. data/lib/smithy-client/handler.rb +29 -0
  19. data/lib/smithy-client/handler_builder.rb +33 -0
  20. data/lib/smithy-client/handler_context.rb +67 -0
  21. data/lib/smithy-client/handler_list.rb +197 -0
  22. data/lib/smithy-client/handler_list_entry.rb +102 -0
  23. data/lib/smithy-client/http/error_inspector.rb +87 -0
  24. data/lib/smithy-client/http/headers.rb +122 -0
  25. data/lib/smithy-client/http/request.rb +57 -0
  26. data/lib/smithy-client/http/response.rb +178 -0
  27. data/lib/smithy-client/http_api_key_provider.rb +18 -0
  28. data/lib/smithy-client/http_bearer_provider.rb +18 -0
  29. data/lib/smithy-client/http_login_provider.rb +19 -0
  30. data/lib/smithy-client/identities/anonymous.rb +10 -0
  31. data/lib/smithy-client/identities/http_api_key.rb +18 -0
  32. data/lib/smithy-client/identities/http_bearer.rb +18 -0
  33. data/lib/smithy-client/identities/http_login.rb +22 -0
  34. data/lib/smithy-client/identity.rb +15 -0
  35. data/lib/smithy-client/log_formatter.rb +215 -0
  36. data/lib/smithy-client/log_param_filter.rb +88 -0
  37. data/lib/smithy-client/log_param_formatter.rb +65 -0
  38. data/lib/smithy-client/managed_file.rb +14 -0
  39. data/lib/smithy-client/net_http/connection_pool.rb +297 -0
  40. data/lib/smithy-client/net_http/handler.rb +160 -0
  41. data/lib/smithy-client/net_http/patches.rb +28 -0
  42. data/lib/smithy-client/networking_error.rb +16 -0
  43. data/lib/smithy-client/pageable_response.rb +138 -0
  44. data/lib/smithy-client/param_converter.rb +243 -0
  45. data/lib/smithy-client/param_validator.rb +213 -0
  46. data/lib/smithy-client/plugin.rb +144 -0
  47. data/lib/smithy-client/plugin_list.rb +141 -0
  48. data/lib/smithy-client/plugins/anonymous_auth.rb +23 -0
  49. data/lib/smithy-client/plugins/checksum_required.rb +51 -0
  50. data/lib/smithy-client/plugins/content_length.rb +26 -0
  51. data/lib/smithy-client/plugins/default_params.rb +22 -0
  52. data/lib/smithy-client/plugins/host_prefix.rb +69 -0
  53. data/lib/smithy-client/plugins/http_api_key_auth.rb +37 -0
  54. data/lib/smithy-client/plugins/http_basic_auth.rb +47 -0
  55. data/lib/smithy-client/plugins/http_bearer_auth.rb +37 -0
  56. data/lib/smithy-client/plugins/http_digest_auth.rb +60 -0
  57. data/lib/smithy-client/plugins/idempotency_token.rb +34 -0
  58. data/lib/smithy-client/plugins/logging.rb +56 -0
  59. data/lib/smithy-client/plugins/net_http.rb +163 -0
  60. data/lib/smithy-client/plugins/pageable_response.rb +37 -0
  61. data/lib/smithy-client/plugins/param_converter.rb +32 -0
  62. data/lib/smithy-client/plugins/param_validator.rb +30 -0
  63. data/lib/smithy-client/plugins/protocol.rb +66 -0
  64. data/lib/smithy-client/plugins/raise_response_errors.rb +33 -0
  65. data/lib/smithy-client/plugins/request_compression.rb +200 -0
  66. data/lib/smithy-client/plugins/response_target.rb +71 -0
  67. data/lib/smithy-client/plugins/retry_errors.rb +125 -0
  68. data/lib/smithy-client/plugins/sign_requests.rb +24 -0
  69. data/lib/smithy-client/plugins/stub_responses.rb +102 -0
  70. data/lib/smithy-client/protocol_spec_matcher.rb +60 -0
  71. data/lib/smithy-client/refreshing_identity_provider.rb +65 -0
  72. data/lib/smithy-client/request.rb +76 -0
  73. data/lib/smithy-client/response.rb +48 -0
  74. data/lib/smithy-client/retry/adaptive.rb +66 -0
  75. data/lib/smithy-client/retry/client_rate_limiter.rb +142 -0
  76. data/lib/smithy-client/retry/quota.rb +58 -0
  77. data/lib/smithy-client/retry/standard.rb +52 -0
  78. data/lib/smithy-client/retry.rb +36 -0
  79. data/lib/smithy-client/rpc_v2_cbor/protocol.rb +38 -0
  80. data/lib/smithy-client/rpc_v2_cbor/request_builder.rb +76 -0
  81. data/lib/smithy-client/rpc_v2_cbor/response_parser.rb +86 -0
  82. data/lib/smithy-client/rpc_v2_cbor/response_stubber.rb +34 -0
  83. data/lib/smithy-client/service_error.rb +57 -0
  84. data/lib/smithy-client/signer.rb +16 -0
  85. data/lib/smithy-client/signers/anonymous.rb +13 -0
  86. data/lib/smithy-client/signers/http_api_key.rb +52 -0
  87. data/lib/smithy-client/signers/http_basic.rb +23 -0
  88. data/lib/smithy-client/signers/http_bearer.rb +19 -0
  89. data/lib/smithy-client/signers/http_digest.rb +21 -0
  90. data/lib/smithy-client/stubbing/data_applicator.rb +61 -0
  91. data/lib/smithy-client/stubbing/empty_stub.rb +69 -0
  92. data/lib/smithy-client/stubbing/endpoint_provider.rb +22 -0
  93. data/lib/smithy-client/stubbing/protocol.rb +29 -0
  94. data/lib/smithy-client/stubbing/stub_data.rb +25 -0
  95. data/lib/smithy-client/stubbing.rb +14 -0
  96. data/lib/smithy-client/stubs.rb +212 -0
  97. data/lib/smithy-client/util.rb +15 -0
  98. data/lib/smithy-client/waiters/poller.rb +93 -0
  99. data/lib/smithy-client/waiters/waiter.rb +113 -0
  100. data/lib/smithy-client.rb +66 -1
  101. metadata +163 -9
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+
5
+ module Smithy
6
+ module Client
7
+ # @api private
8
+ class ParamValidator
9
+ include Smithy::Schema::Shapes
10
+
11
+ EXPECTED_GOT = 'expected %s to be %s, got class %s instead.'
12
+
13
+ def initialize(ref, validate_required: true)
14
+ @ref = ref
15
+ @validate_required = validate_required
16
+ end
17
+
18
+ # @param [Hash] params
19
+ # @param [String] context
20
+ # @return [void]
21
+ # @raise [ArgumentError] if the params are invalid
22
+ def validate!(params, context: 'params')
23
+ errors = []
24
+ structure(@ref, params, errors, context)
25
+ raise ArgumentError, error_messages(errors) unless errors.empty?
26
+ end
27
+
28
+ private
29
+
30
+ # rubocop:disable Metrics
31
+ def shape(ref, value, errors, context)
32
+ case ref.shape
33
+ when StructureShape then structure(ref, value, errors, context)
34
+ when ListShape then list(ref, value, errors, context)
35
+ when MapShape then map(ref, value, errors, context)
36
+ when DocumentShape then document(ref, value, errors, context)
37
+ when UnionShape then union(ref, value, errors, context)
38
+ when StringShape, EnumShape
39
+ errors << expected_got(context, 'a String', value) unless value.is_a?(String)
40
+ when IntegerShape, IntEnumShape
41
+ errors << expected_got(context, 'an Integer', value) unless value.is_a?(Integer)
42
+ when BigDecimalShape
43
+ errors << expected_got(context, 'a BigDecimal', value) unless value.is_a?(BigDecimal)
44
+ when FloatShape
45
+ errors << expected_got(context, 'a Float', value) unless value.is_a?(Float)
46
+ when TimestampShape
47
+ errors << expected_got(context, 'a Time object', value) unless value.is_a?(Time)
48
+ when BooleanShape
49
+ errors << expected_got(context, 'true or false', value) unless [true, false].include?(value)
50
+ when BlobShape
51
+ unless value.is_a?(String)
52
+ if streaming_input?(ref)
53
+ unless io_like?(value)
54
+ errors << expected_got(
55
+ context,
56
+ 'a String or IO like object that supports read and rewind',
57
+ value
58
+ )
59
+ end
60
+ elsif !io_like?(value, require_size: true)
61
+ errors << expected_got(
62
+ context,
63
+ 'a String or IO like object that supports read, rewind, and size',
64
+ value
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end
70
+ # rubocop:enable Metrics
71
+
72
+ def document(shape, value, errors, context)
73
+ document_types = [Hash, Array, Numeric, String, TrueClass, FalseClass, NilClass]
74
+ unless document_types.any? { |t| value.is_a?(t) }
75
+ errors << expected_got(context, "one of #{document_types.join(', ')}", value)
76
+ end
77
+
78
+ case value
79
+ when Hash
80
+ value.each do |k, v|
81
+ document(shape, v, errors, context + "[#{k}]")
82
+ end
83
+ when Array
84
+ value.each do |v|
85
+ document(shape, v, errors, context)
86
+ end
87
+ end
88
+ end
89
+
90
+ def list(ref, values, errors, context)
91
+ unless values.is_a?(Array)
92
+ errors << expected_got(context, 'an Array', values)
93
+ return
94
+ end
95
+
96
+ values.each.with_index do |value, index|
97
+ next unless value
98
+
99
+ shape(ref.shape.member, value, errors, context + "[#{index}]")
100
+ end
101
+ end
102
+
103
+ def map(ref, values, errors, context)
104
+ unless values.is_a?(Hash)
105
+ errors << expected_got(context, 'a Hash', values)
106
+ return
107
+ end
108
+
109
+ values.each do |key, value|
110
+ shape(ref.shape.key, key, errors, "#{context} #{key.inspect} key")
111
+ next unless value
112
+
113
+ shape(ref.shape.value, value, errors, context + "[#{key.inspect}]")
114
+ end
115
+ end
116
+
117
+ def member(ref, name, value, errors, context)
118
+ if ref.shape.member?(name)
119
+ member_ref = ref.shape.member(name)
120
+ shape(member_ref, value, errors, context + "[#{name.inspect}]")
121
+ else
122
+ errors << "unexpected value at #{context}[#{name.inspect}]"
123
+ end
124
+ end
125
+
126
+ def structure(ref, values, errors, context)
127
+ return if ref.shape == Prelude::Unit
128
+ return unless valid_structure?(ref, values, errors, context)
129
+
130
+ validate_required_members(ref, values, errors, context) if @validate_required
131
+ values.each_pair do |name, value|
132
+ next if value.nil?
133
+
134
+ member(ref, name, value, errors, context)
135
+ end
136
+ end
137
+
138
+ def valid_structure?(ref, values, errors, context)
139
+ if !values.is_a?(Hash) && !values.is_a?(ref.shape.type)
140
+ errors << expected_got(context, 'a Hash', values)
141
+ return false
142
+ end
143
+
144
+ true
145
+ end
146
+
147
+ def union(ref, values, errors, context)
148
+ return unless valid_union?(ref, values, errors, context)
149
+
150
+ if values.is_a?(Schema::Union)
151
+ _name, member_ref = ref.shape.member_by_type(values.class)
152
+ shape(member_ref, values.value, errors, context)
153
+ else
154
+ values.each_pair do |name, value|
155
+ next if value.nil?
156
+
157
+ member(ref, name, value, errors, context)
158
+ end
159
+ end
160
+ end
161
+
162
+ def valid_union?(ref, values, errors, context)
163
+ return true if values.is_a?(ref.shape.type)
164
+
165
+ unless values.is_a?(Hash)
166
+ errors << expected_got(context, 'a Hash', values)
167
+ return false
168
+ end
169
+ return true if values.size <= 1
170
+
171
+ union_members = ref.shape.members.keys.join(', ')
172
+ error = "expected #{context} to be a Hash with one of #{union_members}, got #{values.size} keys instead."
173
+ errors << error
174
+ false
175
+ end
176
+
177
+ def validate_required_members(ref, values, errors, context)
178
+ ref.shape.members.each do |name, member_ref|
179
+ traits = member_ref.traits
180
+ next unless traits.key?('smithy.api#required') && !traits.key?('smithy.api#clientOptional')
181
+
182
+ if values[name].nil?
183
+ param = "#{context}[#{name.inspect}]"
184
+ errors << "missing required parameter #{param}"
185
+ end
186
+ end
187
+ end
188
+
189
+ def streaming_input?(ref)
190
+ ref.shape.traits.key?('smithy.api#streaming')
191
+ end
192
+
193
+ def io_like?(value, require_size: false)
194
+ value.respond_to?(:read) && value.respond_to?(:rewind) &&
195
+ (!require_size || value.respond_to?(:size))
196
+ end
197
+
198
+ def error_messages(errors)
199
+ if errors.size == 1
200
+ errors.first
201
+ else
202
+ prefix = "\n - "
203
+ "parameter validator found #{errors.size} errors:" +
204
+ prefix + errors.join(prefix)
205
+ end
206
+ end
207
+
208
+ def expected_got(context, expected, got)
209
+ format(EXPECTED_GOT, context, expected, got.class.name)
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Client
5
+ # Base plugin that all plugins should inherit from.
6
+ #
7
+ # @example
8
+ # class MyPlugin < Plugin
9
+ # option :my_option, default: 'default'
10
+ #
11
+ # def add_handlers(handlers, _config)
12
+ # handlers.add(Handler)
13
+ # end
14
+ #
15
+ # class Handler < Client::Handler
16
+ # def call(context)
17
+ # ...
18
+ # end
19
+ # end
20
+ # end
21
+ class Plugin
22
+ extend HandlerBuilder
23
+
24
+ # @param [Configuration] config
25
+ # @return [void]
26
+ def add_options(config)
27
+ self.class.options.each do |option|
28
+ if option.default_block
29
+ config.add_option(option.name, &option.default_block)
30
+ else
31
+ config.add_option(option.name, option.default)
32
+ end
33
+ end
34
+ end
35
+
36
+ # @param [HandlerList] handlers
37
+ # @param [Struct] _config
38
+ # @return [void]
39
+ def add_handlers(handlers, _config)
40
+ handlers.copy_from(self.class.handlers)
41
+ end
42
+
43
+ # @param [Class<Base>] client_class
44
+ # @param [Hash] options
45
+ # @return [void]
46
+ def before_initialize(client_class, options)
47
+ self.class.before_initialize_hooks.each do |block|
48
+ block.call(client_class, options)
49
+ end
50
+ end
51
+
52
+ # @param [Base] client
53
+ # @return [void]
54
+ def after_initialize(client)
55
+ self.class.after_initialize_hooks.each do |block|
56
+ block.call(client)
57
+ end
58
+ end
59
+
60
+ class << self
61
+ # (see PluginOption#initialize)
62
+ def option(name, options = {}, &block)
63
+ options[:default_block] = block if block_given?
64
+ self.options << PluginOption.new(name, options)
65
+ end
66
+
67
+ def before_initialize(&block)
68
+ before_initialize_hooks << block
69
+ end
70
+
71
+ def after_initialize(&block)
72
+ after_initialize_hooks << block
73
+ end
74
+
75
+ # @return [Array<PluginOption>]
76
+ def options
77
+ @options ||= []
78
+ end
79
+
80
+ # @return [HandlerList]
81
+ def handlers
82
+ @handlers ||= HandlerList.new
83
+ end
84
+
85
+ # @return [Array<Proc>]
86
+ def before_initialize_hooks
87
+ @before_initialize_hooks ||= []
88
+ end
89
+
90
+ # @return [Array<Proc>]
91
+ def after_initialize_hooks
92
+ @after_initialize_hooks ||= []
93
+ end
94
+ end
95
+
96
+ # Holds the configuration for a plugin option.
97
+ class PluginOption
98
+ # @param [Symbol] name
99
+ # @param [Hash] options
100
+ # @option options [Object] :default
101
+ # @option options [Proc] :default_block Can also be set by passing a block.
102
+ # @option options [String] :doc_type
103
+ # @option options [String] :doc_default
104
+ # @option options [String] :docstring
105
+ # @option options [String] :rbs_type
106
+ def initialize(name, options = {})
107
+ @name = name
108
+ # prevent unstable object shapes by ensuring
109
+ # order and presence of instance variables
110
+ @default = nil
111
+ @default_block = nil
112
+ @doc_default = nil
113
+ @doc_type = nil
114
+ @docstring = nil
115
+ @rbs_type = nil
116
+ options.each_pair do |opt_name, opt_value|
117
+ send("#{opt_name}=", opt_value)
118
+ end
119
+ end
120
+
121
+ # @return [Symbol]
122
+ attr_reader :name
123
+
124
+ # @return [Object, nil]
125
+ attr_accessor :default
126
+
127
+ # @return [Proc, nil]
128
+ attr_accessor :default_block
129
+
130
+ # @return [String, nil]
131
+ attr_accessor :doc_default
132
+
133
+ # @return [String, nil]
134
+ attr_accessor :doc_type
135
+
136
+ # @return [String, nil]
137
+ attr_accessor :docstring
138
+
139
+ # @return [String, nil]
140
+ attr_accessor :rbs_type
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Client
5
+ # A list of plugins.
6
+ class PluginList
7
+ include Enumerable
8
+
9
+ # Initializes the plugin list with the given plugins.
10
+ # @param [Array, Set, PluginList] plugins
11
+ def initialize(plugins = [])
12
+ @mutex = Mutex.new
13
+ @plugins = Set.new
14
+ if plugins.is_a?(PluginList)
15
+ plugins.send(:each_plugin) { |plugin| _add(plugin) }
16
+ else
17
+ plugins.each { |plugin| _add(plugin) }
18
+ end
19
+ end
20
+
21
+ # Enumerates the plugins.
22
+ # @return [Enumerator]
23
+ def each
24
+ each_plugin do |plugin_wrapper|
25
+ yield(plugin_wrapper.plugin)
26
+ end
27
+ end
28
+
29
+ # Adds and returns the `plugin`.
30
+ # @param [Plugin] plugin
31
+ # @return [void]
32
+ def add(plugin)
33
+ @mutex.synchronize do
34
+ _add(plugin)
35
+ end
36
+ nil
37
+ end
38
+
39
+ # Removes and returns the `plugin`.
40
+ # @param [Plugin] plugin
41
+ # @return [void]
42
+ def remove(plugin)
43
+ @mutex.synchronize do
44
+ @plugins.delete(PluginWrapper.new(plugin))
45
+ end
46
+ nil
47
+ end
48
+
49
+ # Replaces the existing list of plugins.
50
+ # @param [Array<Plugin>] plugins
51
+ # @return [void]
52
+ def set(plugins)
53
+ @mutex.synchronize do
54
+ @plugins.clear
55
+ plugins.each do |plugin|
56
+ _add(plugin)
57
+ end
58
+ end
59
+ nil
60
+ end
61
+
62
+ private
63
+
64
+ # Not safe to call outside the mutex.
65
+ def _add(plugin)
66
+ @plugins << PluginWrapper.new(plugin)
67
+ end
68
+
69
+ # Yield each PluginDetail behind the mutex
70
+ def each_plugin(&block)
71
+ @mutex.synchronize do
72
+ @plugins.each(&block)
73
+ end
74
+ end
75
+
76
+ # A utility class that computes the canonical name for a plugin
77
+ # and defers requiring the plugin until the plugin class is
78
+ # required.
79
+ # @api private
80
+ class PluginWrapper
81
+ # @param [String, Symbol, Module, Class] plugin
82
+ def initialize(plugin)
83
+ case plugin
84
+ when Module
85
+ @canonical_name = plugin.name || plugin.object_id
86
+ @plugin = plugin
87
+ when Symbol, String
88
+ words = plugin.to_s.split('.')
89
+ @canonical_name = words.pop
90
+ @gem_name = words.empty? ? nil : words.join('.')
91
+ @plugin = nil
92
+ else
93
+ @canonical_name = plugin.object_id
94
+ @plugin = plugin
95
+ end
96
+ end
97
+
98
+ # @return [String]
99
+ attr_reader :canonical_name
100
+
101
+ # @return [Class<Plugin>]
102
+ def plugin
103
+ @plugin ||= require_plugin
104
+ end
105
+
106
+ # Returns the given plugin if it is already a PluginWrapper.
107
+ def self.new(plugin)
108
+ if plugin.is_a?(self)
109
+ plugin
110
+ else
111
+ super
112
+ end
113
+ end
114
+
115
+ # @return [Boolean]
116
+ # @api private
117
+ def eql?(other)
118
+ canonical_name == other.canonical_name
119
+ end
120
+
121
+ # @return [Integer]
122
+ # @api private
123
+ def hash
124
+ canonical_name.hash
125
+ end
126
+
127
+ private
128
+
129
+ # @return [Class<Plugin>]
130
+ def require_plugin
131
+ require(@gem_name) if @gem_name
132
+ plugin_class = Plugin
133
+ @canonical_name.split('::').each do |const_name|
134
+ plugin_class = plugin_class.const_get(const_name)
135
+ end
136
+ plugin_class
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../anonymous_provider'
4
+ require_relative '../identities/anonymous'
5
+ require_relative '../signers/anonymous'
6
+ require_relative '../auth_schemes/anonymous'
7
+
8
+ module Smithy
9
+ module Client
10
+ module Plugins
11
+ # @api private
12
+ class AnonymousAuth < Plugin
13
+ option(:anonymous_provider) do |_config|
14
+ AnonymousProvider.new
15
+ end
16
+
17
+ option(:anonymous_auth_scheme) do |_config|
18
+ Smithy::Client::AuthSchemes::Anonymous.new
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module Smithy
6
+ module Client
7
+ module Plugins
8
+ # @api private
9
+ class ChecksumRequired < Plugin
10
+ def add_handlers(handlers, _config)
11
+ # Ensure checksum is computed AFTER the request is built but BEFORE it is signed
12
+ handlers.add(Handler, priority: 15)
13
+ end
14
+
15
+ # @api private
16
+ class Handler < Client::Handler
17
+ CHUNK_SIZE = 1 * 1024 * 1024 # one MB
18
+
19
+ def call(context)
20
+ if checksum_required_operation?(context)
21
+ context.http_request.headers['Content-Md5'] ||= md5(context.http_request.body)
22
+ end
23
+ @handler.call(context)
24
+ end
25
+
26
+ private
27
+
28
+ def checksum_required_operation?(context)
29
+ context.operation.traits.key?('smithy.api#httpChecksumRequired')
30
+ end
31
+
32
+ def md5(value)
33
+ md5 = OpenSSL::Digest.new('MD5')
34
+ update_in_chunks(md5, value)
35
+ md5.base64digest
36
+ end
37
+
38
+ def update_in_chunks(digest, io)
39
+ loop do
40
+ chunk = io.read(CHUNK_SIZE)
41
+ break unless chunk
42
+
43
+ digest.update(chunk)
44
+ end
45
+ io.rewind
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Client
5
+ module Plugins
6
+ # @api private
7
+ class ContentLength < Plugin
8
+ # @api private
9
+ class Handler < Client::Handler
10
+ # https://github.com/ruby/net-http/blob/master/lib/net/http/requests.rb
11
+ METHODS_WITH_BODY = Set.new(%w[POST PUT PATCH PROPFIND PROPPATCH MKCOL LOCK UNLOCK])
12
+
13
+ def call(context)
14
+ body = context.http_request.body
15
+ if body.respond_to?(:size) && METHODS_WITH_BODY.include?(context.http_request.http_method)
16
+ context.http_request.headers['Content-Length'] = body.size
17
+ end
18
+ @handler.call(context)
19
+ end
20
+ end
21
+
22
+ handler(Handler, step: :sign, priority: 10)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Client
5
+ module Plugins
6
+ # @api private
7
+ class DefaultParams < Plugin
8
+ def add_handlers(handlers, _config)
9
+ handlers.add(Handler, step: :initialize)
10
+ end
11
+
12
+ # @api private
13
+ class Handler < Client::Handler
14
+ def call(context)
15
+ Client::DefaultParams.new(context.operation.input).apply(context.params)
16
+ @handler.call(context)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smithy
4
+ module Client
5
+ module Plugins
6
+ # @api private
7
+ class HostPrefix < Plugin
8
+ option(
9
+ :disable_host_prefix_injection,
10
+ default: false,
11
+ doc_type: 'Boolean',
12
+ docstring: 'When `true`, the SDK will not prepend the modeled host prefix to the endpoint.'
13
+ ) do |_config|
14
+ value = ENV['DISABLE_HOST_PREFIX_INJECTION'] || 'false'
15
+ Util.str_to_bool(value)
16
+ end
17
+
18
+ def after_initialize(client)
19
+ validate_disable_host_prefix_injection(client.config)
20
+ end
21
+
22
+ def validate_disable_host_prefix_injection(config)
23
+ return if [true, false].include?(config.disable_host_prefix_injection)
24
+
25
+ raise ArgumentError,
26
+ ':disable_host_prefix_injection must be either `true` or `false`'
27
+ end
28
+
29
+ def add_handlers(handlers, config)
30
+ handlers.add(Handler, priority: 25) unless config.disable_host_prefix_injection
31
+ end
32
+
33
+ # @api private
34
+ class Handler < Smithy::Client::Handler
35
+ def call(context)
36
+ host_prefix = context.operation.traits.dig('smithy.api#endpoint', 'hostPrefix')
37
+ apply_host_prefix(context, host_prefix) if host_prefix
38
+ @handler.call(context)
39
+ end
40
+
41
+ private
42
+
43
+ # TODO: optimize this to collect all labels in one pass
44
+ def apply_host_prefix(context, host_prefix)
45
+ input = context.operation.input
46
+ prefix = host_prefix.gsub(/\{.+?}/) do |label|
47
+ label_value(input, label.delete('{}'), context.params)
48
+ end
49
+ context.http_request.endpoint.host = prefix + context.http_request.endpoint.host
50
+ end
51
+
52
+ def label_value(input, label, params)
53
+ name = nil
54
+ input.shape.members.each do |member_name, member_ref|
55
+ next unless member_ref.traits.key?('smithy.api#hostLabel')
56
+ next unless member_ref.member_name == label
57
+
58
+ name = member_name
59
+ end
60
+ raise ArgumentError, "#{label} is not a valid host label" if name.nil?
61
+ raise ArgumentError, "params[:#{name}] must not be nil or blank" if params[name].nil? || params[name].empty?
62
+
63
+ params[name]
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end