httpx 0.15.4 → 0.18.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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_16_0.md +93 -0
  3. data/doc/release_notes/0_16_1.md +5 -0
  4. data/doc/release_notes/0_17_0.md +49 -0
  5. data/doc/release_notes/0_18_0.md +69 -0
  6. data/lib/httpx/adapters/datadog.rb +1 -1
  7. data/lib/httpx/adapters/faraday.rb +8 -14
  8. data/lib/httpx/adapters/webmock.rb +9 -3
  9. data/lib/httpx/altsvc.rb +2 -2
  10. data/lib/httpx/buffer.rb +1 -1
  11. data/lib/httpx/callbacks.rb +1 -1
  12. data/lib/httpx/chainable.rb +18 -11
  13. data/lib/httpx/connection/http1.rb +21 -13
  14. data/lib/httpx/connection/http2.rb +20 -25
  15. data/lib/httpx/connection.rb +73 -77
  16. data/lib/httpx/domain_name.rb +1 -1
  17. data/lib/httpx/errors.rb +11 -11
  18. data/lib/httpx/extensions.rb +50 -4
  19. data/lib/httpx/headers.rb +1 -1
  20. data/lib/httpx/io/ssl.rb +3 -3
  21. data/lib/httpx/io/tls.rb +8 -8
  22. data/lib/httpx/loggable.rb +5 -5
  23. data/lib/httpx/options.rb +108 -81
  24. data/lib/httpx/parser/http1.rb +11 -7
  25. data/lib/httpx/plugins/aws_sdk_authentication.rb +42 -18
  26. data/lib/httpx/plugins/aws_sigv4.rb +19 -20
  27. data/lib/httpx/plugins/compression.rb +17 -14
  28. data/lib/httpx/plugins/cookies/cookie.rb +4 -2
  29. data/lib/httpx/plugins/cookies/jar.rb +21 -2
  30. data/lib/httpx/plugins/cookies.rb +20 -7
  31. data/lib/httpx/plugins/digest_authentication.rb +19 -15
  32. data/lib/httpx/plugins/expect.rb +26 -18
  33. data/lib/httpx/plugins/follow_redirects.rb +9 -9
  34. data/lib/httpx/plugins/grpc/call.rb +4 -1
  35. data/lib/httpx/plugins/grpc/message.rb +2 -2
  36. data/lib/httpx/plugins/grpc.rb +72 -46
  37. data/lib/httpx/plugins/h2c.rb +7 -3
  38. data/lib/httpx/plugins/internal_telemetry.rb +8 -8
  39. data/lib/httpx/plugins/multipart/decoder.rb +187 -0
  40. data/lib/httpx/plugins/multipart/mime_type_detector.rb +3 -3
  41. data/lib/httpx/plugins/multipart/part.rb +2 -2
  42. data/lib/httpx/plugins/multipart.rb +16 -2
  43. data/lib/httpx/plugins/ntlm_authentication.rb +12 -10
  44. data/lib/httpx/plugins/proxy/socks4.rb +2 -1
  45. data/lib/httpx/plugins/proxy/socks5.rb +2 -1
  46. data/lib/httpx/plugins/proxy/ssh.rb +20 -13
  47. data/lib/httpx/plugins/proxy.rb +10 -10
  48. data/lib/httpx/plugins/response_cache/store.rb +55 -0
  49. data/lib/httpx/plugins/response_cache.rb +88 -0
  50. data/lib/httpx/plugins/retries.rb +46 -23
  51. data/lib/httpx/plugins/stream.rb +3 -4
  52. data/lib/httpx/plugins/upgrade.rb +7 -6
  53. data/lib/httpx/pool.rb +39 -13
  54. data/lib/httpx/registry.rb +2 -2
  55. data/lib/httpx/request.rb +16 -25
  56. data/lib/httpx/resolver/https.rb +4 -8
  57. data/lib/httpx/resolver/native.rb +19 -5
  58. data/lib/httpx/resolver/resolver_mixin.rb +2 -1
  59. data/lib/httpx/resolver/system.rb +2 -0
  60. data/lib/httpx/resolver.rb +2 -2
  61. data/lib/httpx/response.rb +91 -48
  62. data/lib/httpx/selector.rb +11 -24
  63. data/lib/httpx/session.rb +41 -23
  64. data/lib/httpx/session2.rb +23 -0
  65. data/lib/httpx/timers.rb +84 -0
  66. data/lib/httpx/transcoder/body.rb +3 -2
  67. data/lib/httpx/transcoder/chunker.rb +2 -1
  68. data/lib/httpx/transcoder/form.rb +20 -0
  69. data/lib/httpx/transcoder/json.rb +12 -0
  70. data/lib/httpx/transcoder.rb +62 -1
  71. data/lib/httpx/utils.rb +10 -2
  72. data/lib/httpx/version.rb +1 -1
  73. data/lib/httpx.rb +7 -3
  74. data/sig/buffer.rbs +3 -1
  75. data/sig/chainable.rbs +31 -29
  76. data/sig/connection/http1.rbs +11 -5
  77. data/sig/connection/http2.rbs +16 -5
  78. data/sig/connection.rbs +31 -13
  79. data/sig/errors.rbs +35 -1
  80. data/sig/headers.rbs +20 -19
  81. data/sig/httpx.rbs +4 -1
  82. data/sig/loggable.rbs +3 -1
  83. data/sig/options.rbs +45 -34
  84. data/sig/parser/http1.rbs +3 -3
  85. data/sig/plugins/authentication.rbs +1 -1
  86. data/sig/plugins/aws_sdk_authentication.rbs +25 -3
  87. data/sig/plugins/aws_sigv4.rbs +13 -5
  88. data/sig/plugins/basic_authentication.rbs +1 -1
  89. data/sig/plugins/compression.rbs +4 -6
  90. data/sig/plugins/cookies/cookie.rbs +5 -7
  91. data/sig/plugins/cookies/jar.rbs +9 -10
  92. data/sig/plugins/cookies.rbs +4 -5
  93. data/sig/plugins/digest_authentication.rbs +2 -3
  94. data/sig/plugins/expect.rbs +2 -4
  95. data/sig/plugins/follow_redirects.rbs +3 -5
  96. data/sig/plugins/grpc.rbs +4 -7
  97. data/sig/plugins/h2c.rbs +0 -2
  98. data/sig/plugins/multipart.rbs +64 -10
  99. data/sig/plugins/ntlm_authentication.rbs +2 -3
  100. data/sig/plugins/persistent.rbs +3 -8
  101. data/sig/plugins/proxy/ssh.rbs +4 -4
  102. data/sig/plugins/proxy.rbs +13 -13
  103. data/sig/plugins/push_promise.rbs +0 -2
  104. data/sig/plugins/response_cache.rbs +35 -0
  105. data/sig/plugins/retries.rbs +7 -8
  106. data/sig/plugins/stream.rbs +1 -1
  107. data/sig/plugins/upgrade.rbs +2 -3
  108. data/sig/pool.rbs +7 -2
  109. data/sig/registry.rbs +1 -1
  110. data/sig/request.rbs +11 -8
  111. data/sig/resolver/native.rbs +10 -5
  112. data/sig/resolver/resolver_mixin.rbs +4 -5
  113. data/sig/resolver/system.rbs +4 -0
  114. data/sig/resolver.rbs +7 -0
  115. data/sig/response.rbs +26 -13
  116. data/sig/selector.rbs +11 -9
  117. data/sig/session.rbs +22 -23
  118. data/sig/timers.rbs +32 -0
  119. data/sig/transcoder/body.rbs +6 -1
  120. data/sig/transcoder/chunker.rbs +8 -2
  121. data/sig/transcoder/form.rbs +3 -1
  122. data/sig/transcoder/json.rbs +2 -0
  123. data/sig/transcoder.rbs +13 -5
  124. data/sig/utils.rbs +6 -0
  125. metadata +18 -18
  126. data/lib/httpx/request2.rb +0 -14
data/lib/httpx/options.rb CHANGED
@@ -30,6 +30,7 @@ module HTTPX
30
30
  :request_body_class => Class.new(Request::Body),
31
31
  :response_body_class => Class.new(Response::Body),
32
32
  :connection_class => Class.new(Connection),
33
+ :options_class => Class.new(self),
33
34
  :transport => nil,
34
35
  :transport_options => nil,
35
36
  :addresses => nil,
@@ -38,71 +39,100 @@ module HTTPX
38
39
  :resolver_options => { cache: true },
39
40
  }.freeze
40
41
 
42
+ begin
43
+ module HashExtensions
44
+ refine Hash do
45
+ def >=(other)
46
+ Hash[other] <= self
47
+ end
48
+
49
+ def <=(other)
50
+ other = Hash[other]
51
+ return false unless size <= other.size
52
+
53
+ each do |k, v|
54
+ v2 = other.fetch(k) { return false }
55
+ return false unless v2 == v
56
+ end
57
+ true
58
+ end
59
+ end
60
+ end
61
+ using HashExtensions
62
+ end unless Hash.method_defined?(:>=)
63
+
41
64
  class << self
42
65
  def new(options = {})
43
66
  # let enhanced options go through
44
- return options if self == Options && options.class > self
67
+ return options if self == Options && options.class < self
45
68
  return options if options.is_a?(self)
46
69
 
47
70
  super
48
71
  end
49
72
 
50
- def def_option(name, layout = nil, &interpreter)
51
- attr_reader name
73
+ def method_added(meth)
74
+ super
52
75
 
53
- if layout
54
- class_eval(<<-OUT, __FILE__, __LINE__ + 1)
55
- def #{name}=(value)
56
- return if value.nil?
76
+ return unless meth =~ /^option_(.+)$/
57
77
 
58
- value = begin
59
- #{layout}
60
- end
78
+ optname = Regexp.last_match(1).to_sym
61
79
 
62
- @#{name} = value
63
- end
80
+ attr_reader(optname)
81
+ end
82
+
83
+ def def_option(optname, *args, &block)
84
+ if args.size.zero? && !block
85
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
86
+ def option_#{optname}(v); v; end # def option_smth(v); v; end
64
87
  OUT
88
+ return
89
+ end
65
90
 
66
- elsif interpreter
67
- define_method(:"#{name}=") do |value|
68
- return if value.nil?
91
+ deprecated_def_option(optname, *args, &block)
92
+ end
93
+
94
+ def deprecated_def_option(optname, layout = nil, &interpreter)
95
+ warn "DEPRECATION WARNING: using `def_option(#{optname})` for setting options is deprecated. " \
96
+ "Define module OptionsMethods and `def option_#{optname}(val)` instead."
69
97
 
70
- instance_variable_set(:"@#{name}", instance_exec(value, &interpreter))
98
+ if layout
99
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
100
+ def option_#{optname}(value) # def option_origin(v)
101
+ #{layout} # URI(v)
102
+ end # end
103
+ OUT
104
+ elsif interpreter
105
+ define_method(:"option_#{optname}") do |value|
106
+ instance_exec(value, &interpreter)
71
107
  end
72
- else
73
- attr_writer name
74
108
  end
75
-
76
- protected :"#{name}="
77
109
  end
78
110
  end
79
111
 
80
112
  def initialize(options = {})
81
113
  defaults = DEFAULT_OPTIONS.merge(options)
82
- defaults.each do |(k, v)|
114
+ defaults.each do |k, v|
83
115
  next if v.nil?
84
116
 
85
117
  begin
86
- __send__(:"#{k}=", v)
118
+ value = __send__(:"option_#{k}", v)
119
+ instance_variable_set(:"@#{k}", value)
87
120
  rescue NoMethodError
88
121
  raise Error, "unknown option: #{k}"
89
122
  end
90
123
  end
124
+ freeze
91
125
  end
92
126
 
93
- def_option(:origin, <<-OUT)
127
+ def option_origin(value)
94
128
  URI(value)
95
- OUT
129
+ end
96
130
 
97
- def_option(:headers, <<-OUT)
98
- if self.headers
99
- self.headers.merge(value)
100
- else
101
- Headers.new(value)
102
- end
103
- OUT
131
+ def option_headers(value)
132
+ Headers.new(value)
133
+ end
104
134
 
105
- def_option(:timeout, <<-OUT)
135
+ def option_timeout(value)
106
136
  timeouts = Hash[value]
107
137
 
108
138
  if timeouts.key?(:loop_timeout)
@@ -111,42 +141,43 @@ module HTTPX
111
141
  end
112
142
 
113
143
  timeouts
114
- OUT
144
+ end
115
145
 
116
- def_option(:max_concurrent_requests, <<-OUT)
117
- raise Error, ":max_concurrent_requests must be positive" unless value.positive?
146
+ def option_max_concurrent_requests(value)
147
+ raise TypeError, ":max_concurrent_requests must be positive" unless value.positive?
118
148
 
119
149
  value
120
- OUT
150
+ end
121
151
 
122
- def_option(:max_requests, <<-OUT)
123
- raise Error, ":max_requests must be positive" unless value.positive?
152
+ def option_max_requests(value)
153
+ raise TypeError, ":max_requests must be positive" unless value.positive?
124
154
 
125
155
  value
126
- OUT
156
+ end
127
157
 
128
- def_option(:window_size, <<-OUT)
158
+ def option_window_size(value)
129
159
  Integer(value)
130
- OUT
160
+ end
131
161
 
132
- def_option(:body_threshold_size, <<-OUT)
162
+ def option_body_threshold_size(value)
133
163
  Integer(value)
134
- OUT
164
+ end
135
165
 
136
- def_option(:transport, <<-OUT)
166
+ def option_transport(value)
137
167
  transport = value.to_s
138
- raise Error, "\#{transport} is an unsupported transport type" unless IO.registry.key?(transport)
168
+ raise TypeError, "\#{transport} is an unsupported transport type" unless IO.registry.key?(transport)
139
169
 
140
170
  transport
141
- OUT
171
+ end
142
172
 
143
- def_option(:addresses, <<-OUT)
173
+ def option_addresses(value)
144
174
  Array(value)
145
- OUT
175
+ end
146
176
 
147
177
  %i[
148
178
  params form json body ssl http2_settings
149
- request_class response_class headers_class request_body_class response_body_class connection_class
179
+ request_class response_class headers_class request_body_class
180
+ response_body_class connection_class options_class
150
181
  io fallback_protocol debug debug_level transport_options resolver_class resolver_options
151
182
  persistent
152
183
  ].each do |method_name|
@@ -154,6 +185,7 @@ module HTTPX
154
185
  end
155
186
 
156
187
  REQUEST_IVARS = %i[@params @form @json @body].freeze
188
+ private_constant :REQUEST_IVARS
157
189
 
158
190
  def ==(other)
159
191
  ivars = instance_variables | other.instance_variables
@@ -171,18 +203,17 @@ module HTTPX
171
203
  end
172
204
 
173
205
  def merge(other)
174
- raise ArgumentError, "#{other.inspect} is not a valid set of options" unless other.respond_to?(:to_hash)
206
+ raise ArgumentError, "#{other} is not a valid set of options" unless other.respond_to?(:to_hash)
175
207
 
176
208
  h2 = other.to_hash
177
209
  return self if h2.empty?
178
210
 
179
211
  h1 = to_hash
180
212
 
181
- return self if h1 == h2
213
+ return self if h1 >= h2
182
214
 
183
- merged = h1.merge(h2) do |k, v1, v2|
184
- case k
185
- when :headers, :ssl, :http2_settings, :timeout
215
+ merged = h1.merge(h2) do |_k, v1, v2|
216
+ if v1.respond_to?(:merge) && v2.respond_to?(:merge)
186
217
  v1.merge(v2)
187
218
  else
188
219
  v2
@@ -193,34 +224,30 @@ module HTTPX
193
224
  end
194
225
 
195
226
  def to_hash
196
- hash_pairs = instance_variables.map do |ivar|
197
- [ivar[1..-1].to_sym, instance_variable_get(ivar)]
227
+ instance_variables.each_with_object({}) do |ivar, hs|
228
+ hs[ivar[1..-1].to_sym] = instance_variable_get(ivar)
229
+ end
230
+ end
231
+
232
+ if RUBY_VERSION > "2.4.0"
233
+ def initialize_dup(other)
234
+ instance_variables.each do |ivar|
235
+ instance_variable_set(ivar, other.instance_variable_get(ivar).dup)
236
+ end
237
+ end
238
+ else
239
+ def initialize_dup(other)
240
+ instance_variables.each do |ivar|
241
+ value = other.instance_variable_get(ivar)
242
+ value = case value
243
+ when Symbol, Fixnum, TrueClass, FalseClass # rubocop:disable Lint/UnifiedInteger
244
+ value
245
+ else
246
+ value.dup
247
+ end
248
+ instance_variable_set(ivar, value)
249
+ end
198
250
  end
199
- Hash[hash_pairs]
200
- end
201
-
202
- def initialize_dup(other)
203
- self.headers = other.headers.dup
204
- self.ssl = other.ssl.dup
205
- self.request_class = other.request_class.dup
206
- self.response_class = other.response_class.dup
207
- self.headers_class = other.headers_class.dup
208
- self.request_body_class = other.request_body_class.dup
209
- self.response_body_class = other.response_body_class.dup
210
- self.connection_class = other.connection_class.dup
211
- end
212
-
213
- def freeze
214
- super
215
-
216
- headers.freeze
217
- ssl.freeze
218
- request_class.freeze
219
- response_class.freeze
220
- headers_class.freeze
221
- request_body_class.freeze
222
- response_body_class.freeze
223
- connection_class.freeze
224
251
  end
225
252
  end
226
253
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module HTTPX
4
4
  module Parser
5
- Error = Class.new(Error)
5
+ class Error < Error; end
6
6
 
7
7
  class HTTP1
8
8
  VERSIONS = %w[1.0 1.1].freeze
@@ -60,7 +60,7 @@ module HTTPX
60
60
  (m = %r{\AHTTP(?:/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?}in.match(@buffer)) ||
61
61
  raise(Error, "wrong head line format")
62
62
  version, code, _ = m.captures
63
- raise(Error, "unsupported HTTP version (HTTP/#{version})") unless VERSIONS.include?(version)
63
+ raise(Error, "unsupported HTTP version (HTTP/#{version})") unless version && VERSIONS.include?(version)
64
64
 
65
65
  @http_version = version.split(".").map(&:to_i)
66
66
  @status_code = code.to_i
@@ -72,9 +72,14 @@ module HTTPX
72
72
 
73
73
  def parse_headers
74
74
  headers = @headers
75
- while (idx = @buffer.index("\n"))
76
- line = @buffer.byteslice(0..idx).sub(/\s+\z/, "")
77
- @buffer = @buffer.byteslice((idx + 1)..-1)
75
+ buffer = @buffer
76
+
77
+ while (idx = buffer.index("\n"))
78
+ line = buffer.byteslice(0..idx)
79
+ raise Error, "wrong header format" if line.start_with?("\s", "\t")
80
+
81
+ line.lstrip!
82
+ buffer = @buffer = buffer.byteslice((idx + 1)..-1)
78
83
  if line.empty?
79
84
  case @state
80
85
  when :headers
@@ -97,9 +102,8 @@ module HTTPX
97
102
  raise Error, "wrong header format" unless separator_index
98
103
 
99
104
  key = line.byteslice(0..(separator_index - 1))
100
- raise Error, "wrong header format" if key.start_with?("\s", "\t")
101
105
 
102
- key.strip!
106
+ key.rstrip! # was lstripped previously!
103
107
  value = line.byteslice((separator_index + 1)..-1)
104
108
  value.strip!
105
109
  raise Error, "wrong header format" if value.nil?
@@ -8,6 +8,23 @@ module HTTPX
8
8
  # It requires the "aws-sdk-core" gem.
9
9
  #
10
10
  module AwsSdkAuthentication
11
+ # Mock configuration, to be used only when resolving credentials
12
+ class Configuration
13
+ attr_reader :profile
14
+
15
+ def initialize(profile)
16
+ @profile = profile
17
+ end
18
+
19
+ def respond_to_missing?(*)
20
+ true
21
+ end
22
+
23
+ def method_missing(*)
24
+ nil
25
+ end
26
+ end
27
+
11
28
  #
12
29
  # encapsulates access to an AWS SDK credentials store.
13
30
  #
@@ -30,23 +47,8 @@ module HTTPX
30
47
  end
31
48
 
32
49
  class << self
33
- attr_reader :credentials, :region
34
-
35
50
  def load_dependencies(_klass)
36
51
  require "aws-sdk-core"
37
-
38
- client = Class.new(Seahorse::Client::Base) do
39
- @identifier = :httpx
40
- set_api(Aws::S3::ClientApi::API)
41
- add_plugin(Aws::Plugins::CredentialsConfiguration)
42
- add_plugin(Aws::Plugins::RegionalEndpoint)
43
- class << self
44
- attr_reader :identifier
45
- end
46
- end.new
47
-
48
- @credentials = Credentials.new(client.config[:credentials])
49
- @region = client.config[:region]
50
52
  end
51
53
 
52
54
  def configure(klass)
@@ -56,6 +58,26 @@ module HTTPX
56
58
  def extra_options(options)
57
59
  options.merge(max_concurrent_requests: 1)
58
60
  end
61
+
62
+ def credentials(profile)
63
+ mock_configuration = Configuration.new(profile)
64
+ Credentials.new(Aws::CredentialProviderChain.new(mock_configuration).resolve)
65
+ end
66
+
67
+ def region(profile)
68
+ # https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-core/lib/aws-sdk-core/plugins/regional_endpoint.rb#L62
69
+ keys = %w[AWS_REGION AMAZON_REGION AWS_DEFAULT_REGION]
70
+ env_region = ENV.values_at(*keys).compact.first
71
+ env_region = nil if env_region == ""
72
+ cfg_region = Aws.shared_config.region(profile: profile)
73
+ env_region || cfg_region
74
+ end
75
+ end
76
+
77
+ module OptionsMethods
78
+ def option_aws_profile(value)
79
+ String(value)
80
+ end
59
81
  end
60
82
 
61
83
  module InstanceMethods
@@ -64,9 +86,11 @@ module HTTPX
64
86
  # aws_authentication(credentials: Aws::Credentials.new('akid', 'secret'))
65
87
  # aws_authentication()
66
88
  #
67
- def aws_sdk_authentication(**options)
68
- credentials = AwsSdkAuthentication.credentials
69
- region = AwsSdkAuthentication.region
89
+ def aws_sdk_authentication(
90
+ credentials: AwsSdkAuthentication.credentials(@options.aws_profile),
91
+ region: AwsSdkAuthentication.region(@options.aws_profile),
92
+ **options
93
+ )
70
94
 
71
95
  aws_sigv4_authentication(
72
96
  credentials: credentials,
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
- require "aws-sdk-s3"
5
-
6
3
  module HTTPX
7
4
  module Plugins
8
5
  #
@@ -75,16 +72,16 @@ module HTTPX
75
72
 
76
73
  # canonical request
77
74
  creq = "#{request.verb.to_s.upcase}" \
78
- "\n#{request.canonical_path}" \
79
- "\n#{request.canonical_query}" \
80
- "\n#{canonical_headers}" \
81
- "\n#{signed_headers}" \
82
- "\n#{content_hashed}"
75
+ "\n#{request.canonical_path}" \
76
+ "\n#{request.canonical_query}" \
77
+ "\n#{canonical_headers}" \
78
+ "\n#{signed_headers}" \
79
+ "\n#{content_hashed}"
83
80
 
84
81
  credential_scope = "#{date}" \
85
- "/#{@region}" \
86
- "/#{@service}" \
87
- "/#{lower_provider_prefix}_request"
82
+ "/#{@region}" \
83
+ "/#{@service}" \
84
+ "/#{lower_provider_prefix}_request"
88
85
 
89
86
  algo_line = "#{upper_provider_prefix}-HMAC-#{@algorithm}"
90
87
  # string to sign
@@ -141,22 +138,24 @@ module HTTPX
141
138
  end
142
139
 
143
140
  class << self
144
- def extra_options(options)
145
- Class.new(options.class) do
146
- def_option(:sigv4_signer, <<-OUT)
147
- value.is_a?(#{Signer}) ? value : #{Signer}.new(value)
148
- OUT
149
- end.new(options)
150
- end
151
-
152
- def load_dependencies(klass)
141
+ def load_dependencies(*)
142
+ require "set"
153
143
  require "digest/sha2"
154
144
  require "openssl"
145
+ end
146
+
147
+ def configure(klass)
155
148
  klass.plugin(:expect)
156
149
  klass.plugin(:compression)
157
150
  end
158
151
  end
159
152
 
153
+ module OptionsMethods
154
+ def option_sigv4_signer(value)
155
+ value.is_a?(Signer) ? value : Signer.new(value)
156
+ end
157
+ end
158
+
160
159
  module InstanceMethods
161
160
  def aws_sigv4_authentication(**options)
162
161
  with(sigv4_signer: Signer.new(**options))
@@ -23,21 +23,22 @@ module HTTPX
23
23
  encodings = Module.new do
24
24
  extend Registry
25
25
  end
26
+ options.merge(encodings: encodings)
27
+ end
28
+ end
26
29
 
27
- Class.new(options.class) do
28
- def_option(:compression_threshold_size, <<-OUT)
29
- bytes = Integer(value)
30
- raise Error, ":expect_threshold_size must be positive" unless bytes.positive?
30
+ module OptionsMethods
31
+ def option_compression_threshold_size(value)
32
+ bytes = Integer(value)
33
+ raise TypeError, ":expect_threshold_size must be positive" unless bytes.positive?
31
34
 
32
- bytes
33
- OUT
35
+ bytes
36
+ end
34
37
 
35
- def_option(:encodings, <<-OUT)
36
- raise Error, ":encodings must be a registry" unless value.respond_to?(:registry)
38
+ def option_encodings(value)
39
+ raise TypeError, ":encodings must be a registry" unless value.respond_to?(:registry)
37
40
 
38
- value
39
- OUT
40
- end.new(options).merge(encodings: encodings)
41
+ value
41
42
  end
42
43
  end
43
44
 
@@ -71,6 +72,8 @@ module HTTPX
71
72
  end
72
73
 
73
74
  module ResponseBodyMethods
75
+ using ArrayExtensions
76
+
74
77
  attr_reader :encodings
75
78
 
76
79
  def initialize(*)
@@ -89,7 +92,7 @@ module HTTPX
89
92
  Float::INFINITY
90
93
  end
91
94
 
92
- @_inflaters = @headers.get("content-encoding").map do |encoding|
95
+ @_inflaters = @headers.get("content-encoding").filter_map do |encoding|
93
96
  next if encoding == "identity"
94
97
 
95
98
  inflater = @options.encodings.registry(encoding).inflater(compressed_length)
@@ -99,7 +102,7 @@ module HTTPX
99
102
 
100
103
  @encodings << encoding
101
104
  inflater
102
- end.compact
105
+ end
103
106
 
104
107
  # this can happen if the only declared encoding is "identity"
105
108
  remove_instance_variable(:@_inflaters) if @_inflaters.empty?
@@ -133,7 +136,7 @@ module HTTPX
133
136
  end
134
137
 
135
138
  def each(&blk)
136
- return enum_for(__method__) unless block_given?
139
+ return enum_for(__method__) unless blk
137
140
 
138
141
  return deflate(&blk) if @buffer.size.zero?
139
142
 
@@ -43,7 +43,7 @@ module HTTPX
43
43
  # Precedence: 1. longer path 2. older creation
44
44
  (@name <=> other.name).nonzero? ||
45
45
  (other.path.length <=> @path.length).nonzero? ||
46
- (@created_at <=> other.created_at).nonzero?
46
+ (@created_at <=> other.created_at).nonzero? || 0
47
47
  end
48
48
 
49
49
  class << self
@@ -107,6 +107,8 @@ module HTTPX
107
107
 
108
108
  @path ||= "/"
109
109
  raise ArgumentError, "name must be specified" if @name.nil?
110
+
111
+ @name = @name.to_s
110
112
  end
111
113
 
112
114
  def expires
@@ -122,7 +124,7 @@ module HTTPX
122
124
  # Returns a string for use in the Cookie header, i.e. `name=value`
123
125
  # or `name="value"`.
124
126
  def cookie_value
125
- "#{@name}=#{Scanner.quote(@value)}"
127
+ "#{@name}=#{Scanner.quote(@value.to_s)}"
126
128
  end
127
129
  alias_method :to_s, :cookie_value
128
130
 
@@ -55,9 +55,9 @@ module HTTPX
55
55
  end
56
56
 
57
57
  def each(uri = nil, &blk)
58
- return enum_for(__method__, uri) unless block_given?
58
+ return enum_for(__method__, uri) unless blk
59
59
 
60
- return @store.each(&blk) unless uri
60
+ return @cookies.each(&blk) unless uri
61
61
 
62
62
  uri = URI(uri)
63
63
 
@@ -73,6 +73,25 @@ module HTTPX
73
73
  end
74
74
  end
75
75
  end
76
+
77
+ def merge(other)
78
+ cookies_dup = dup
79
+
80
+ other.each do |elem|
81
+ cookie = case elem
82
+ when Cookie
83
+ elem
84
+ when Array
85
+ Cookie.new(*elem)
86
+ else
87
+ Cookie.new(elem)
88
+ end
89
+
90
+ cookies_dup.add(cookie)
91
+ end
92
+
93
+ cookies_dup
94
+ end
76
95
  end
77
96
  end
78
97
  end
@@ -18,12 +18,10 @@ module HTTPX
18
18
  require "httpx/plugins/cookies/set_cookie_parser"
19
19
  end
20
20
 
21
- def self.extra_options(options)
22
- Class.new(options.class) do
23
- def_option(:cookies, <<-OUT)
24
- value.is_a?(#{Jar}) ? value : #{Jar}.new(value)
25
- OUT
26
- end.new(options)
21
+ module OptionsMethods
22
+ def option_cookies(value)
23
+ value.is_a?(Jar) ? value : Jar.new(value)
24
+ end
27
25
  end
28
26
 
29
27
  module InstanceMethods
@@ -63,7 +61,7 @@ module HTTPX
63
61
 
64
62
  def build_request(*, _)
65
63
  request = super
66
- request.headers.set_cookie(@options.cookies[request.uri])
64
+ request.headers.set_cookie(request.options.cookies[request.uri])
67
65
  request
68
66
  end
69
67
  end
@@ -77,6 +75,21 @@ module HTTPX
77
75
  add("cookie", header_value)
78
76
  end
79
77
  end
78
+
79
+ module OptionsMethods
80
+ def initialize(*)
81
+ super
82
+
83
+ return unless @headers.key?("cookie")
84
+
85
+ @headers.delete("cookie").each do |ck|
86
+ ck.split(/ *; */).each do |cookie|
87
+ name, value = cookie.split("=", 2)
88
+ @cookies.add(Cookie.new(name, value))
89
+ end
90
+ end
91
+ end
92
+ end
80
93
  end
81
94
  register_plugin :cookies, Cookies
82
95
  end