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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +241 -41
- data/LICENSE.txt +1 -1
- data/README.md +110 -13
- data/UPGRADING.md +491 -0
- data/http.gemspec +32 -29
- data/lib/http/base64.rb +11 -1
- data/lib/http/chainable/helpers.rb +62 -0
- data/lib/http/chainable/verbs.rb +136 -0
- data/lib/http/chainable.rb +232 -136
- data/lib/http/client.rb +158 -127
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +126 -97
- data/lib/http/content_type.rb +61 -6
- data/lib/http/errors.rb +25 -1
- data/lib/http/feature.rb +65 -5
- data/lib/http/features/auto_deflate.rb +124 -17
- data/lib/http/features/auto_inflate.rb +38 -15
- data/lib/http/features/caching/entry.rb +178 -0
- data/lib/http/features/caching/in_memory_store.rb +63 -0
- data/lib/http/features/caching.rb +216 -0
- data/lib/http/features/digest_auth.rb +234 -0
- data/lib/http/features/instrumentation.rb +97 -17
- data/lib/http/features/logging.rb +183 -5
- data/lib/http/features/normalize_uri.rb +17 -0
- data/lib/http/features/raise_error.rb +18 -3
- data/lib/http/form_data/composite_io.rb +106 -0
- data/lib/http/form_data/file.rb +95 -0
- data/lib/http/form_data/multipart/param.rb +62 -0
- data/lib/http/form_data/multipart.rb +106 -0
- data/lib/http/form_data/part.rb +52 -0
- data/lib/http/form_data/readable.rb +58 -0
- data/lib/http/form_data/urlencoded.rb +175 -0
- data/lib/http/form_data/version.rb +8 -0
- data/lib/http/form_data.rb +102 -0
- data/lib/http/headers/known.rb +3 -0
- data/lib/http/headers/normalizer.rb +17 -36
- data/lib/http/headers.rb +172 -65
- data/lib/http/mime_type/adapter.rb +24 -9
- data/lib/http/mime_type/json.rb +19 -4
- data/lib/http/mime_type.rb +21 -3
- data/lib/http/options/definitions.rb +189 -0
- data/lib/http/options.rb +172 -125
- data/lib/http/redirector.rb +80 -75
- data/lib/http/request/body.rb +87 -6
- data/lib/http/request/builder.rb +184 -0
- data/lib/http/request/proxy.rb +83 -0
- data/lib/http/request/writer.rb +76 -16
- data/lib/http/request.rb +214 -98
- data/lib/http/response/body.rb +103 -18
- data/lib/http/response/inflater.rb +35 -7
- data/lib/http/response/parser.rb +98 -4
- data/lib/http/response/status/reasons.rb +2 -4
- data/lib/http/response/status.rb +141 -31
- data/lib/http/response.rb +219 -61
- data/lib/http/retriable/delay_calculator.rb +38 -11
- data/lib/http/retriable/errors.rb +21 -0
- data/lib/http/retriable/performer.rb +82 -38
- data/lib/http/session.rb +280 -0
- data/lib/http/timeout/global.rb +147 -34
- data/lib/http/timeout/null.rb +155 -9
- data/lib/http/timeout/per_operation.rb +139 -18
- data/lib/http/uri/normalizer.rb +82 -0
- data/lib/http/uri/parsing.rb +182 -0
- data/lib/http/uri.rb +289 -124
- data/lib/http/version.rb +2 -1
- data/lib/http.rb +11 -2
- data/sig/deps.rbs +122 -0
- data/sig/http.rbs +1619 -0
- data/test/http/base64_test.rb +28 -0
- data/test/http/client_test.rb +739 -0
- data/test/http/connection_test.rb +1533 -0
- data/test/http/content_type_test.rb +190 -0
- data/test/http/errors_test.rb +28 -0
- data/test/http/feature_test.rb +49 -0
- data/test/http/features/auto_deflate_test.rb +317 -0
- data/test/http/features/auto_inflate_test.rb +213 -0
- data/test/http/features/caching_test.rb +942 -0
- data/test/http/features/digest_auth_test.rb +996 -0
- data/test/http/features/instrumentation_test.rb +246 -0
- data/test/http/features/logging_test.rb +654 -0
- data/test/http/features/normalize_uri_test.rb +41 -0
- data/test/http/features/raise_error_test.rb +77 -0
- data/test/http/form_data/composite_io_test.rb +215 -0
- data/test/http/form_data/file_test.rb +255 -0
- data/test/http/form_data/fixtures/the-http-gem.info +1 -0
- data/test/http/form_data/multipart_test.rb +303 -0
- data/test/http/form_data/part_test.rb +90 -0
- data/test/http/form_data/urlencoded_test.rb +164 -0
- data/test/http/form_data_test.rb +232 -0
- data/test/http/headers/normalizer_test.rb +93 -0
- data/test/http/headers_test.rb +888 -0
- data/test/http/mime_type/json_test.rb +39 -0
- data/test/http/mime_type_test.rb +150 -0
- data/test/http/options/base_uri_test.rb +148 -0
- data/test/http/options/body_test.rb +21 -0
- data/test/http/options/features_test.rb +38 -0
- data/test/http/options/form_test.rb +21 -0
- data/test/http/options/headers_test.rb +32 -0
- data/test/http/options/json_test.rb +21 -0
- data/test/http/options/merge_test.rb +78 -0
- data/test/http/options/new_test.rb +37 -0
- data/test/http/options/proxy_test.rb +32 -0
- data/test/http/options_test.rb +575 -0
- data/test/http/redirector_test.rb +639 -0
- data/test/http/request/body_test.rb +318 -0
- data/test/http/request/builder_test.rb +623 -0
- data/test/http/request/writer_test.rb +391 -0
- data/test/http/request_test.rb +1733 -0
- data/test/http/response/body_test.rb +292 -0
- data/test/http/response/parser_test.rb +105 -0
- data/test/http/response/status_test.rb +322 -0
- data/test/http/response_test.rb +502 -0
- data/test/http/retriable/delay_calculator_test.rb +194 -0
- data/test/http/retriable/errors_test.rb +71 -0
- data/test/http/retriable/performer_test.rb +551 -0
- data/test/http/session_test.rb +424 -0
- data/test/http/timeout/global_test.rb +239 -0
- data/test/http/timeout/null_test.rb +218 -0
- data/test/http/timeout/per_operation_test.rb +220 -0
- data/test/http/uri/normalizer_test.rb +89 -0
- data/test/http/uri_test.rb +1140 -0
- data/test/http/version_test.rb +15 -0
- data/test/http_test.rb +818 -0
- data/test/regression_tests.rb +27 -0
- data/test/support/dummy_server/encoding_routes.rb +47 -0
- data/test/support/dummy_server/routes.rb +201 -0
- data/test/support/dummy_server/servlet.rb +81 -0
- data/test/support/dummy_server.rb +200 -0
- data/{spec → test}/support/fakeio.rb +2 -2
- data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
- data/test/support/http_handling_shared/timeout_tests.rb +134 -0
- data/test/support/http_handling_shared.rb +11 -0
- data/test/support/proxy_server.rb +207 -0
- data/test/support/servers/runner.rb +67 -0
- data/{spec → test}/support/simplecov.rb +11 -2
- data/test/support/ssl_helper.rb +108 -0
- data/test/test_helper.rb +38 -0
- metadata +108 -168
- data/.github/workflows/ci.yml +0 -67
- data/.gitignore +0 -15
- data/.rspec +0 -1
- data/.rubocop/layout.yml +0 -8
- data/.rubocop/metrics.yml +0 -4
- data/.rubocop/rspec.yml +0 -9
- data/.rubocop/style.yml +0 -32
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -219
- data/.yardopts +0 -2
- data/CHANGES_OLD.md +0 -1002
- data/Gemfile +0 -51
- data/Guardfile +0 -18
- data/Rakefile +0 -64
- data/lib/http/headers/mixin.rb +0 -34
- data/lib/http/retriable/client.rb +0 -37
- data/logo.png +0 -0
- data/spec/lib/http/client_spec.rb +0 -556
- data/spec/lib/http/connection_spec.rb +0 -88
- data/spec/lib/http/content_type_spec.rb +0 -47
- data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
- data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
- data/spec/lib/http/features/instrumentation_spec.rb +0 -81
- data/spec/lib/http/features/logging_spec.rb +0 -65
- data/spec/lib/http/features/raise_error_spec.rb +0 -62
- data/spec/lib/http/headers/mixin_spec.rb +0 -36
- data/spec/lib/http/headers/normalizer_spec.rb +0 -52
- data/spec/lib/http/headers_spec.rb +0 -527
- data/spec/lib/http/options/body_spec.rb +0 -15
- data/spec/lib/http/options/features_spec.rb +0 -33
- data/spec/lib/http/options/form_spec.rb +0 -15
- data/spec/lib/http/options/headers_spec.rb +0 -24
- data/spec/lib/http/options/json_spec.rb +0 -15
- data/spec/lib/http/options/merge_spec.rb +0 -68
- data/spec/lib/http/options/new_spec.rb +0 -30
- data/spec/lib/http/options/proxy_spec.rb +0 -20
- data/spec/lib/http/options_spec.rb +0 -13
- data/spec/lib/http/redirector_spec.rb +0 -530
- data/spec/lib/http/request/body_spec.rb +0 -211
- data/spec/lib/http/request/writer_spec.rb +0 -121
- data/spec/lib/http/request_spec.rb +0 -234
- data/spec/lib/http/response/body_spec.rb +0 -85
- data/spec/lib/http/response/parser_spec.rb +0 -74
- data/spec/lib/http/response/status_spec.rb +0 -253
- data/spec/lib/http/response_spec.rb +0 -262
- data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
- data/spec/lib/http/retriable/performer_spec.rb +0 -302
- data/spec/lib/http/uri/normalizer_spec.rb +0 -95
- data/spec/lib/http/uri_spec.rb +0 -71
- data/spec/lib/http_spec.rb +0 -535
- data/spec/regression_specs.rb +0 -24
- data/spec/spec_helper.rb +0 -89
- data/spec/support/black_hole.rb +0 -13
- data/spec/support/dummy_server/servlet.rb +0 -203
- data/spec/support/dummy_server.rb +0 -44
- data/spec/support/fuubar.rb +0 -21
- data/spec/support/http_handling_shared.rb +0 -190
- data/spec/support/proxy_server.rb +0 -39
- data/spec/support/servers/config.rb +0 -11
- data/spec/support/servers/runner.rb +0 -19
- data/spec/support/ssl_helper.rb +0 -104
- /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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
112
|
+
# @example
|
|
113
|
+
# def_option_accessor(:timeout, reader_only: false)
|
|
93
114
|
#
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
private
|
|
183
215
|
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
189
|
-
|
|
229
|
+
# Raises an argument error with adjusted backtrace
|
|
230
|
+
#
|
|
231
|
+
# @api private
|
|
232
|
+
# @return [void]
|
|
190
233
|
def argument_error!(message)
|
|
191
|
-
|
|
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"
|