http 5.3.1 → 6.0.0

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +241 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +110 -13
  5. data/UPGRADING.md +491 -0
  6. data/http.gemspec +32 -29
  7. data/lib/http/base64.rb +11 -1
  8. data/lib/http/chainable/helpers.rb +62 -0
  9. data/lib/http/chainable/verbs.rb +136 -0
  10. data/lib/http/chainable.rb +232 -136
  11. data/lib/http/client.rb +158 -127
  12. data/lib/http/connection/internals.rb +141 -0
  13. data/lib/http/connection.rb +126 -97
  14. data/lib/http/content_type.rb +61 -6
  15. data/lib/http/errors.rb +25 -1
  16. data/lib/http/feature.rb +65 -5
  17. data/lib/http/features/auto_deflate.rb +124 -17
  18. data/lib/http/features/auto_inflate.rb +38 -15
  19. data/lib/http/features/caching/entry.rb +178 -0
  20. data/lib/http/features/caching/in_memory_store.rb +63 -0
  21. data/lib/http/features/caching.rb +216 -0
  22. data/lib/http/features/digest_auth.rb +234 -0
  23. data/lib/http/features/instrumentation.rb +97 -17
  24. data/lib/http/features/logging.rb +183 -5
  25. data/lib/http/features/normalize_uri.rb +17 -0
  26. data/lib/http/features/raise_error.rb +18 -3
  27. data/lib/http/form_data/composite_io.rb +106 -0
  28. data/lib/http/form_data/file.rb +95 -0
  29. data/lib/http/form_data/multipart/param.rb +62 -0
  30. data/lib/http/form_data/multipart.rb +106 -0
  31. data/lib/http/form_data/part.rb +52 -0
  32. data/lib/http/form_data/readable.rb +58 -0
  33. data/lib/http/form_data/urlencoded.rb +175 -0
  34. data/lib/http/form_data/version.rb +8 -0
  35. data/lib/http/form_data.rb +102 -0
  36. data/lib/http/headers/known.rb +3 -0
  37. data/lib/http/headers/normalizer.rb +17 -36
  38. data/lib/http/headers.rb +172 -65
  39. data/lib/http/mime_type/adapter.rb +24 -9
  40. data/lib/http/mime_type/json.rb +19 -4
  41. data/lib/http/mime_type.rb +21 -3
  42. data/lib/http/options/definitions.rb +189 -0
  43. data/lib/http/options.rb +172 -125
  44. data/lib/http/redirector.rb +80 -75
  45. data/lib/http/request/body.rb +87 -6
  46. data/lib/http/request/builder.rb +184 -0
  47. data/lib/http/request/proxy.rb +83 -0
  48. data/lib/http/request/writer.rb +76 -16
  49. data/lib/http/request.rb +214 -98
  50. data/lib/http/response/body.rb +103 -18
  51. data/lib/http/response/inflater.rb +35 -7
  52. data/lib/http/response/parser.rb +98 -4
  53. data/lib/http/response/status/reasons.rb +2 -4
  54. data/lib/http/response/status.rb +141 -31
  55. data/lib/http/response.rb +219 -61
  56. data/lib/http/retriable/delay_calculator.rb +38 -11
  57. data/lib/http/retriable/errors.rb +21 -0
  58. data/lib/http/retriable/performer.rb +82 -38
  59. data/lib/http/session.rb +280 -0
  60. data/lib/http/timeout/global.rb +147 -34
  61. data/lib/http/timeout/null.rb +155 -9
  62. data/lib/http/timeout/per_operation.rb +139 -18
  63. data/lib/http/uri/normalizer.rb +82 -0
  64. data/lib/http/uri/parsing.rb +182 -0
  65. data/lib/http/uri.rb +289 -124
  66. data/lib/http/version.rb +2 -1
  67. data/lib/http.rb +11 -2
  68. data/sig/deps.rbs +122 -0
  69. data/sig/http.rbs +1619 -0
  70. data/test/http/base64_test.rb +28 -0
  71. data/test/http/client_test.rb +739 -0
  72. data/test/http/connection_test.rb +1533 -0
  73. data/test/http/content_type_test.rb +190 -0
  74. data/test/http/errors_test.rb +28 -0
  75. data/test/http/feature_test.rb +49 -0
  76. data/test/http/features/auto_deflate_test.rb +317 -0
  77. data/test/http/features/auto_inflate_test.rb +213 -0
  78. data/test/http/features/caching_test.rb +942 -0
  79. data/test/http/features/digest_auth_test.rb +996 -0
  80. data/test/http/features/instrumentation_test.rb +246 -0
  81. data/test/http/features/logging_test.rb +654 -0
  82. data/test/http/features/normalize_uri_test.rb +41 -0
  83. data/test/http/features/raise_error_test.rb +77 -0
  84. data/test/http/form_data/composite_io_test.rb +215 -0
  85. data/test/http/form_data/file_test.rb +255 -0
  86. data/test/http/form_data/fixtures/the-http-gem.info +1 -0
  87. data/test/http/form_data/multipart_test.rb +303 -0
  88. data/test/http/form_data/part_test.rb +90 -0
  89. data/test/http/form_data/urlencoded_test.rb +164 -0
  90. data/test/http/form_data_test.rb +232 -0
  91. data/test/http/headers/normalizer_test.rb +93 -0
  92. data/test/http/headers_test.rb +888 -0
  93. data/test/http/mime_type/json_test.rb +39 -0
  94. data/test/http/mime_type_test.rb +150 -0
  95. data/test/http/options/base_uri_test.rb +148 -0
  96. data/test/http/options/body_test.rb +21 -0
  97. data/test/http/options/features_test.rb +38 -0
  98. data/test/http/options/form_test.rb +21 -0
  99. data/test/http/options/headers_test.rb +32 -0
  100. data/test/http/options/json_test.rb +21 -0
  101. data/test/http/options/merge_test.rb +78 -0
  102. data/test/http/options/new_test.rb +37 -0
  103. data/test/http/options/proxy_test.rb +32 -0
  104. data/test/http/options_test.rb +575 -0
  105. data/test/http/redirector_test.rb +639 -0
  106. data/test/http/request/body_test.rb +318 -0
  107. data/test/http/request/builder_test.rb +623 -0
  108. data/test/http/request/writer_test.rb +391 -0
  109. data/test/http/request_test.rb +1733 -0
  110. data/test/http/response/body_test.rb +292 -0
  111. data/test/http/response/parser_test.rb +105 -0
  112. data/test/http/response/status_test.rb +322 -0
  113. data/test/http/response_test.rb +502 -0
  114. data/test/http/retriable/delay_calculator_test.rb +194 -0
  115. data/test/http/retriable/errors_test.rb +71 -0
  116. data/test/http/retriable/performer_test.rb +551 -0
  117. data/test/http/session_test.rb +424 -0
  118. data/test/http/timeout/global_test.rb +239 -0
  119. data/test/http/timeout/null_test.rb +218 -0
  120. data/test/http/timeout/per_operation_test.rb +220 -0
  121. data/test/http/uri/normalizer_test.rb +89 -0
  122. data/test/http/uri_test.rb +1140 -0
  123. data/test/http/version_test.rb +15 -0
  124. data/test/http_test.rb +818 -0
  125. data/test/regression_tests.rb +27 -0
  126. data/test/support/dummy_server/encoding_routes.rb +47 -0
  127. data/test/support/dummy_server/routes.rb +201 -0
  128. data/test/support/dummy_server/servlet.rb +81 -0
  129. data/test/support/dummy_server.rb +200 -0
  130. data/{spec → test}/support/fakeio.rb +2 -2
  131. data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
  132. data/test/support/http_handling_shared/timeout_tests.rb +134 -0
  133. data/test/support/http_handling_shared.rb +11 -0
  134. data/test/support/proxy_server.rb +207 -0
  135. data/test/support/servers/runner.rb +67 -0
  136. data/{spec → test}/support/simplecov.rb +11 -2
  137. data/test/support/ssl_helper.rb +108 -0
  138. data/test/test_helper.rb +38 -0
  139. metadata +108 -168
  140. data/.github/workflows/ci.yml +0 -67
  141. data/.gitignore +0 -15
  142. data/.rspec +0 -1
  143. data/.rubocop/layout.yml +0 -8
  144. data/.rubocop/metrics.yml +0 -4
  145. data/.rubocop/rspec.yml +0 -9
  146. data/.rubocop/style.yml +0 -32
  147. data/.rubocop.yml +0 -11
  148. data/.rubocop_todo.yml +0 -219
  149. data/.yardopts +0 -2
  150. data/CHANGES_OLD.md +0 -1002
  151. data/Gemfile +0 -51
  152. data/Guardfile +0 -18
  153. data/Rakefile +0 -64
  154. data/lib/http/headers/mixin.rb +0 -34
  155. data/lib/http/retriable/client.rb +0 -37
  156. data/logo.png +0 -0
  157. data/spec/lib/http/client_spec.rb +0 -556
  158. data/spec/lib/http/connection_spec.rb +0 -88
  159. data/spec/lib/http/content_type_spec.rb +0 -47
  160. data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
  161. data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
  162. data/spec/lib/http/features/instrumentation_spec.rb +0 -81
  163. data/spec/lib/http/features/logging_spec.rb +0 -65
  164. data/spec/lib/http/features/raise_error_spec.rb +0 -62
  165. data/spec/lib/http/headers/mixin_spec.rb +0 -36
  166. data/spec/lib/http/headers/normalizer_spec.rb +0 -52
  167. data/spec/lib/http/headers_spec.rb +0 -527
  168. data/spec/lib/http/options/body_spec.rb +0 -15
  169. data/spec/lib/http/options/features_spec.rb +0 -33
  170. data/spec/lib/http/options/form_spec.rb +0 -15
  171. data/spec/lib/http/options/headers_spec.rb +0 -24
  172. data/spec/lib/http/options/json_spec.rb +0 -15
  173. data/spec/lib/http/options/merge_spec.rb +0 -68
  174. data/spec/lib/http/options/new_spec.rb +0 -30
  175. data/spec/lib/http/options/proxy_spec.rb +0 -20
  176. data/spec/lib/http/options_spec.rb +0 -13
  177. data/spec/lib/http/redirector_spec.rb +0 -530
  178. data/spec/lib/http/request/body_spec.rb +0 -211
  179. data/spec/lib/http/request/writer_spec.rb +0 -121
  180. data/spec/lib/http/request_spec.rb +0 -234
  181. data/spec/lib/http/response/body_spec.rb +0 -85
  182. data/spec/lib/http/response/parser_spec.rb +0 -74
  183. data/spec/lib/http/response/status_spec.rb +0 -253
  184. data/spec/lib/http/response_spec.rb +0 -262
  185. data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
  186. data/spec/lib/http/retriable/performer_spec.rb +0 -302
  187. data/spec/lib/http/uri/normalizer_spec.rb +0 -95
  188. data/spec/lib/http/uri_spec.rb +0 -71
  189. data/spec/lib/http_spec.rb +0 -535
  190. data/spec/regression_specs.rb +0 -24
  191. data/spec/spec_helper.rb +0 -89
  192. data/spec/support/black_hole.rb +0 -13
  193. data/spec/support/dummy_server/servlet.rb +0 -203
  194. data/spec/support/dummy_server.rb +0 -44
  195. data/spec/support/fuubar.rb +0 -21
  196. data/spec/support/http_handling_shared.rb +0 -190
  197. data/spec/support/proxy_server.rb +0 -39
  198. data/spec/support/servers/config.rb +0 -11
  199. data/spec/support/servers/runner.rb +0 -19
  200. data/spec/support/ssl_helper.rb +0 -104
  201. /data/{spec → test}/support/capture_warning.rb +0 -0
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTP
4
+ # Configuration options for HTTP requests and clients
5
+ class Options
6
+ def_option :headers do |new_headers|
7
+ headers.merge(new_headers) # steep:ignore
8
+ end
9
+
10
+ def_option :encoding do |encoding|
11
+ self.encoding = Encoding.find(encoding) # steep:ignore
12
+ end
13
+
14
+ def_option :features, reader_only: true do |new_features|
15
+ # Normalize features from:
16
+ #
17
+ # [{feature_one: {opt: 'val'}}, :feature_two]
18
+ #
19
+ # into:
20
+ #
21
+ # {feature_one: {opt: 'val'}, feature_two: {}}
22
+ acc = {} #: Hash[untyped, untyped]
23
+ normalized_features = new_features.each_with_object(acc) do |feature, h|
24
+ if feature.is_a?(Hash)
25
+ h.merge!(feature)
26
+ else
27
+ h[feature] = {} # steep:ignore
28
+ end
29
+ end
30
+
31
+ features.merge(normalized_features) # steep:ignore
32
+ end
33
+
34
+ # Sets and normalizes features hash
35
+ #
36
+ # @param [Hash] features
37
+ # @api private
38
+ # @return [Hash]
39
+ def features=(features)
40
+ result = {} #: Hash[Symbol, Feature]
41
+ @features = features.each_with_object(result) do |(name, opts_or_feature), h|
42
+ h[name] = if opts_or_feature.is_a?(Feature)
43
+ opts_or_feature
44
+ else
45
+ unless (feature = self.class.available_features[name])
46
+ argument_error! "Unsupported feature: #{name}"
47
+ end
48
+ feature.new(**opts_or_feature) # steep:ignore
49
+ end
50
+ end
51
+ end
52
+
53
+ %w[
54
+ proxy params form json body response
55
+ socket_class nodelay ssl_socket_class ssl_context ssl
56
+ keep_alive_timeout timeout_class timeout_options
57
+ ].each do |method_name|
58
+ def_option method_name
59
+ end
60
+
61
+ def_option :follow, reader_only: true
62
+
63
+ # Sets follow redirect options
64
+ #
65
+ # @param [Boolean, Hash, nil] value
66
+ # @api private
67
+ # @return [Hash, nil]
68
+ def follow=(value)
69
+ @follow =
70
+ if !value then nil
71
+ elsif true == value then {} #: Hash[untyped, untyped]
72
+ elsif value.respond_to?(:fetch) then value
73
+ else argument_error! "Unsupported follow options: #{value}"
74
+ end
75
+ end
76
+
77
+ def_option :retriable, reader_only: true
78
+
79
+ # Sets retriable options
80
+ #
81
+ # @param [Boolean, Hash, nil] value
82
+ # @api private
83
+ # @return [Hash, nil]
84
+ def retriable=(value)
85
+ @retriable =
86
+ if !value then nil
87
+ elsif true == value then {} #: Hash[untyped, untyped]
88
+ elsif value.respond_to?(:fetch) then value
89
+ else argument_error! "Unsupported retriable options: #{value}"
90
+ end
91
+ end
92
+
93
+ def_option :base_uri, reader_only: true
94
+
95
+ # Sets the base URI for resolving relative request paths
96
+ #
97
+ # @param [String, HTTP::URI, nil] value
98
+ # @api private
99
+ # @return [HTTP::URI, nil]
100
+ def base_uri=(value)
101
+ @base_uri = value ? parse_base_uri(value) : nil
102
+ validate_base_uri_and_persistent!
103
+ end
104
+
105
+ # Checks whether a base URI is set
106
+ #
107
+ # @example
108
+ # opts = HTTP::Options.new(base_uri: "https://example.com")
109
+ # opts.base_uri?
110
+ #
111
+ # @api public
112
+ # @return [Boolean]
113
+ def base_uri?
114
+ !base_uri.nil?
115
+ end
116
+
117
+ def_option :persistent, reader_only: true
118
+
119
+ # Sets persistent connection origin
120
+ #
121
+ # @param [String, nil] value
122
+ # @api private
123
+ # @return [String, nil]
124
+ def persistent=(value)
125
+ @persistent = value ? URI.parse(value).origin : nil
126
+ validate_base_uri_and_persistent!
127
+ end
128
+
129
+ # Checks whether persistent connection is enabled
130
+ #
131
+ # @example
132
+ # opts = HTTP::Options.new(persistent: "http://example.com")
133
+ # opts.persistent?
134
+ #
135
+ # @api public
136
+ # @return [Boolean]
137
+ def persistent?
138
+ !persistent.nil?
139
+ end
140
+
141
+ private
142
+
143
+ # Parses and validates a base URI value
144
+ #
145
+ # @param [String, HTTP::URI] value the base URI to parse
146
+ # @api private
147
+ # @return [HTTP::URI]
148
+ def parse_base_uri(value)
149
+ uri = URI.parse(value)
150
+
151
+ base = @base_uri
152
+ return resolve_base_uri(base, uri) if base
153
+
154
+ argument_error!(format("Invalid base URI: %s", value)) unless uri.scheme
155
+ uri
156
+ end
157
+
158
+ # Resolves a relative URI against an existing base URI
159
+ #
160
+ # @param [HTTP::URI] base the existing base URI
161
+ # @param [HTTP::URI] relative the URI to join
162
+ # @api private
163
+ # @return [HTTP::URI]
164
+ def resolve_base_uri(base, relative)
165
+ unless base.path.end_with?("/")
166
+ base = base.dup
167
+ base.path = "#{base.path}/"
168
+ end
169
+
170
+ URI.parse(base.join(relative))
171
+ end
172
+
173
+ # Validates that base URI and persistent origin are compatible
174
+ #
175
+ # @api private
176
+ # @return [void]
177
+ def validate_base_uri_and_persistent!
178
+ base = @base_uri
179
+ persistent = @persistent
180
+ return unless base && persistent
181
+ return if base.origin.eql?(persistent)
182
+
183
+ argument_error!(
184
+ format("Persistence origin (%s) conflicts with base URI origin (%s)",
185
+ persistent, base.origin)
186
+ )
187
+ end
188
+ end
189
+ end
data/lib/http/options.rb CHANGED
@@ -6,189 +6,236 @@ require "socket"
6
6
  require "http/uri"
7
7
 
8
8
  module HTTP
9
- class Options # rubocop:disable Metrics/ClassLength
9
+ # Configuration options for HTTP requests and clients
10
+ class Options
10
11
  @default_socket_class = TCPSocket
11
12
  @default_ssl_socket_class = OpenSSL::SSL::SSLSocket
12
13
  @default_timeout_class = HTTP::Timeout::Null
13
14
  @available_features = {}
14
15
 
15
16
  class << self
16
- attr_accessor :default_socket_class, :default_ssl_socket_class, :default_timeout_class
17
+ # Default TCP socket class
18
+ #
19
+ # @example
20
+ # HTTP::Options.default_socket_class # => TCPSocket
21
+ #
22
+ # @return [Class] default socket class
23
+ # @api public
24
+ attr_accessor :default_socket_class
25
+
26
+ # Default SSL socket class
27
+ #
28
+ # @example
29
+ # HTTP::Options.default_ssl_socket_class
30
+ #
31
+ # @return [Class] default SSL socket class
32
+ # @api public
33
+ attr_accessor :default_ssl_socket_class
34
+
35
+ # Default timeout handler class
36
+ #
37
+ # @example
38
+ # HTTP::Options.default_timeout_class
39
+ #
40
+ # @return [Class] default timeout class
41
+ # @api public
42
+ attr_accessor :default_timeout_class
43
+
44
+ # Registered feature implementations
45
+ #
46
+ # @example
47
+ # HTTP::Options.available_features
48
+ #
49
+ # @return [Hash] registered feature implementations
50
+ # @api public
17
51
  attr_reader :available_features
18
52
 
19
- def new(options = {})
20
- options.is_a?(self) ? options : super
53
+ # Returns existing Options or creates new one
54
+ #
55
+ # @example
56
+ # HTTP::Options.new(response: :auto)
57
+ #
58
+ # @param [HTTP::Options, Hash, nil] options existing Options or Hash to convert
59
+ # @api public
60
+ # @return [HTTP::Options]
61
+ def new(options = nil, **kwargs)
62
+ return options if options.is_a?(self)
63
+
64
+ super(**(options || kwargs)) # steep:ignore
21
65
  end
22
66
 
67
+ # Returns list of defined option names
68
+ #
69
+ # @example
70
+ # HTTP::Options.defined_options
71
+ #
72
+ # @api semipublic
73
+ # @return [Array<Symbol>]
23
74
  def defined_options
24
75
  @defined_options ||= []
25
76
  end
26
77
 
78
+ # Registers a feature by name and implementation
79
+ #
80
+ # @example
81
+ # HTTP::Options.register_feature(:auto_inflate, AutoInflate)
82
+ #
83
+ # @param [Symbol] name
84
+ # @param [Class] impl
85
+ # @api public
86
+ # @return [Class]
27
87
  def register_feature(name, impl)
28
88
  @available_features[name] = impl
29
89
  end
30
90
 
31
91
  protected
32
92
 
93
+ # Defines an option with accessor and with_ method
94
+ #
95
+ # @param [Symbol] name
96
+ # @param [Boolean] reader_only
97
+ # @api private
98
+ # @return [void]
33
99
  def def_option(name, reader_only: false, &interpreter)
34
100
  defined_options << name.to_sym
35
101
  interpreter ||= ->(v) { v }
36
102
 
37
- if reader_only
38
- attr_reader name
39
- else
40
- attr_accessor name
41
- protected :"#{name}="
42
- end
103
+ def_option_accessor(name, reader_only: reader_only)
43
104
 
44
105
  define_method(:"with_#{name}") do |value|
45
- dup { |opts| opts.send(:"#{name}=", instance_exec(value, &interpreter)) }
106
+ dup { |opts| opts.send(:"#{name}=", instance_exec(value, &interpreter)) } # steep:ignore
46
107
  end
47
108
  end
48
- end
49
-
50
- def initialize(options = {})
51
- defaults = {
52
- :response => :auto,
53
- :proxy => {},
54
- :timeout_class => self.class.default_timeout_class,
55
- :timeout_options => {},
56
- :socket_class => self.class.default_socket_class,
57
- :nodelay => false,
58
- :ssl_socket_class => self.class.default_ssl_socket_class,
59
- :ssl => {},
60
- :keep_alive_timeout => 5,
61
- :headers => {},
62
- :cookies => {},
63
- :encoding => nil,
64
- :features => {}
65
- }
66
-
67
- opts_w_defaults = defaults.merge(options)
68
- opts_w_defaults[:headers] = HTTP::Headers.coerce(opts_w_defaults[:headers])
69
- opts_w_defaults.each { |(k, v)| self[k] = v }
70
- end
71
109
 
72
- def_option :headers do |new_headers|
73
- headers.merge(new_headers)
74
- end
75
-
76
- def_option :cookies do |new_cookies|
77
- new_cookies.each_with_object cookies.dup do |(k, v), jar|
78
- cookie = k.is_a?(Cookie) ? k : Cookie.new(k.to_s, v.to_s)
79
- jar[cookie.name] = cookie.cookie_value
80
- end
81
- end
82
-
83
- def_option :encoding do |encoding|
84
- self.encoding = Encoding.find(encoding)
85
- end
86
-
87
- def_option :features, :reader_only => true do |new_features|
88
- # Normalize features from:
89
- #
90
- # [{feature_one: {opt: 'val'}}, :feature_two]
110
+ # Define accessor methods for an option
91
111
  #
92
- # into:
112
+ # @example
113
+ # def_option_accessor(:timeout, reader_only: false)
93
114
  #
94
- # {feature_one: {opt: 'val'}, feature_two: {}}
95
- normalized_features = new_features.each_with_object({}) do |feature, h|
96
- if feature.is_a?(Hash)
97
- h.merge!(feature)
115
+ # @return [void]
116
+ # @api private
117
+ def def_option_accessor(name, reader_only:)
118
+ if reader_only
119
+ attr_reader name
98
120
  else
99
- h[feature] = {}
121
+ attr_accessor name
122
+ protected :"#{name}="
100
123
  end
101
124
  end
102
-
103
- features.merge(normalized_features)
104
- end
105
-
106
- def features=(features)
107
- @features = features.each_with_object({}) do |(name, opts_or_feature), h|
108
- h[name] = if opts_or_feature.is_a?(Feature)
109
- opts_or_feature
110
- else
111
- unless (feature = self.class.available_features[name])
112
- argument_error! "Unsupported feature: #{name}"
113
- end
114
- feature.new(**opts_or_feature)
115
- end
116
- end
117
- end
118
-
119
- %w[
120
- proxy params form json body response
121
- socket_class nodelay ssl_socket_class ssl_context ssl
122
- keep_alive_timeout timeout_class timeout_options
123
- ].each do |method_name|
124
- def_option method_name
125
- end
126
-
127
- def_option :follow, :reader_only => true
128
-
129
- def follow=(value)
130
- @follow =
131
- case
132
- when !value then nil
133
- when true == value then {}
134
- when value.respond_to?(:fetch) then value
135
- else argument_error! "Unsupported follow options: #{value}"
136
- end
137
- end
138
-
139
- def_option :persistent, :reader_only => true
140
-
141
- def persistent=(value)
142
- @persistent = value ? HTTP::URI.parse(value).origin : nil
143
- end
144
-
145
- def persistent?
146
- !persistent.nil?
147
125
  end
148
126
 
127
+ # Initializes options with keyword arguments
128
+ #
129
+ # @example
130
+ # HTTP::Options.new(response: :auto, follow: true)
131
+ #
132
+ # @api public
133
+ # @return [HTTP::Options]
134
+ def initialize(
135
+ response: :auto,
136
+ encoding: nil,
137
+ nodelay: false,
138
+ keep_alive_timeout: 5,
139
+ proxy: {},
140
+ ssl: {},
141
+ headers: {},
142
+ features: {},
143
+ timeout_class: self.class.default_timeout_class,
144
+ timeout_options: {},
145
+ socket_class: self.class.default_socket_class,
146
+ ssl_socket_class: self.class.default_ssl_socket_class,
147
+ params: nil,
148
+ form: nil,
149
+ json: nil,
150
+ body: nil,
151
+ follow: nil,
152
+ retriable: nil,
153
+ base_uri: nil,
154
+ persistent: nil,
155
+ ssl_context: nil
156
+ )
157
+ assign_options(binding)
158
+ end
159
+
160
+ # Merges two Options objects
161
+ #
162
+ # @example
163
+ # opts = HTTP::Options.new.merge(HTTP::Options.new(response: :body))
164
+ #
165
+ # @param [HTTP::Options, Hash] other
166
+ # @api public
167
+ # @return [HTTP::Options]
149
168
  def merge(other)
150
- h1 = to_hash
151
- h2 = other.to_hash
152
-
153
- merged = h1.merge(h2) do |k, v1, v2|
154
- case k
155
- when :headers
156
- v1.merge(v2)
157
- else
158
- v2
159
- end
169
+ merged = to_hash.merge(other.to_hash) do |k, v1, v2|
170
+ k == :headers ? v1.merge(v2) : v2
160
171
  end
161
172
 
162
- self.class.new(merged)
173
+ self.class.new(**merged)
163
174
  end
164
175
 
176
+ # Converts options to a Hash
177
+ #
178
+ # @example
179
+ # HTTP::Options.new.to_hash
180
+ #
181
+ # @api public
182
+ # @return [Hash]
165
183
  def to_hash
166
- hash_pairs = self.class.
167
- defined_options.
168
- flat_map { |opt_name| [opt_name, send(opt_name)] }
169
- Hash[*hash_pairs]
184
+ self.class.defined_options.to_h { |opt_name| [opt_name, public_send(opt_name)] }
170
185
  end
171
186
 
187
+ # Duplicates the options object
188
+ #
189
+ # @example
190
+ # opts = HTTP::Options.new
191
+ # opts.dup
192
+ #
193
+ # @api public
194
+ # @return [HTTP::Options]
172
195
  def dup
173
196
  dupped = super
174
197
  yield(dupped) if block_given?
175
198
  dupped
176
199
  end
177
200
 
201
+ # Returns a feature by name
202
+ #
203
+ # @example
204
+ # opts = HTTP::Options.new
205
+ # opts.feature(:auto_inflate)
206
+ #
207
+ # @param [Symbol] name
208
+ # @api public
209
+ # @return [Feature, nil]
178
210
  def feature(name)
179
211
  features[name]
180
212
  end
181
213
 
182
- protected
214
+ private
183
215
 
184
- def []=(option, val)
185
- send(:"#{option}=", val)
216
+ # Assigns all option values from the initialize binding
217
+ #
218
+ # @param [Binding] env binding from initialize with keyword argument values
219
+ # @api private
220
+ # @return [void]
221
+ def assign_options(env)
222
+ self.class.defined_options.each do |name|
223
+ value = env.local_variable_get(name)
224
+ value = Headers.coerce(value) if name.eql?(:headers)
225
+ __send__(:"#{name}=", value)
226
+ end
186
227
  end
187
228
 
188
- private
189
-
229
+ # Raises an argument error with adjusted backtrace
230
+ #
231
+ # @api private
232
+ # @return [void]
190
233
  def argument_error!(message)
191
- raise(Error, message, caller(1..-1))
234
+ error = Error.new(message)
235
+ error.set_backtrace(caller(1) || [])
236
+ raise error
192
237
  end
193
238
  end
194
239
  end
240
+
241
+ require "http/options/definitions"