httpx 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_15_2.md +9 -0
  3. data/doc/release_notes/0_15_3.md +5 -0
  4. data/doc/release_notes/0_15_4.md +5 -0
  5. data/doc/release_notes/0_16_0.md +93 -0
  6. data/lib/httpx.rb +6 -3
  7. data/lib/httpx/adapters/faraday.rb +3 -11
  8. data/lib/httpx/buffer.rb +1 -1
  9. data/lib/httpx/callbacks.rb +1 -1
  10. data/lib/httpx/chainable.rb +15 -8
  11. data/lib/httpx/connection.rb +2 -2
  12. data/lib/httpx/connection/http1.rb +3 -1
  13. data/lib/httpx/connection/http2.rb +1 -11
  14. data/lib/httpx/errors.rb +11 -11
  15. data/lib/httpx/io/ssl.rb +2 -2
  16. data/lib/httpx/io/tls.rb +1 -1
  17. data/lib/httpx/loggable.rb +1 -1
  18. data/lib/httpx/options.rb +81 -74
  19. data/lib/httpx/parser/http1.rb +1 -1
  20. data/lib/httpx/plugins/aws_sigv4.rb +10 -9
  21. data/lib/httpx/plugins/compression.rb +12 -11
  22. data/lib/httpx/plugins/cookies.rb +20 -7
  23. data/lib/httpx/plugins/cookies/cookie.rb +4 -3
  24. data/lib/httpx/plugins/cookies/jar.rb +24 -1
  25. data/lib/httpx/plugins/digest_authentication.rb +15 -11
  26. data/lib/httpx/plugins/expect.rb +19 -15
  27. data/lib/httpx/plugins/follow_redirects.rb +9 -9
  28. data/lib/httpx/plugins/grpc.rb +73 -47
  29. data/lib/httpx/plugins/grpc/call.rb +4 -1
  30. data/lib/httpx/plugins/ntlm_authentication.rb +8 -6
  31. data/lib/httpx/plugins/proxy.rb +4 -6
  32. data/lib/httpx/plugins/proxy/socks4.rb +2 -1
  33. data/lib/httpx/plugins/proxy/socks5.rb +2 -1
  34. data/lib/httpx/plugins/proxy/ssh.rb +9 -9
  35. data/lib/httpx/plugins/retries.rb +25 -21
  36. data/lib/httpx/plugins/upgrade.rb +7 -6
  37. data/lib/httpx/registry.rb +1 -1
  38. data/lib/httpx/request.rb +4 -12
  39. data/lib/httpx/resolver/https.rb +0 -2
  40. data/lib/httpx/response.rb +45 -18
  41. data/lib/httpx/selector.rb +13 -12
  42. data/lib/httpx/session.rb +19 -8
  43. data/lib/httpx/session2.rb +21 -0
  44. data/lib/httpx/transcoder/body.rb +1 -1
  45. data/lib/httpx/transcoder/chunker.rb +2 -1
  46. data/lib/httpx/version.rb +1 -1
  47. data/sig/buffer.rbs +2 -0
  48. data/sig/chainable.rbs +24 -28
  49. data/sig/connection.rbs +20 -8
  50. data/sig/connection/http1.rbs +3 -3
  51. data/sig/connection/http2.rbs +1 -1
  52. data/sig/errors.rbs +35 -1
  53. data/sig/headers.rbs +5 -5
  54. data/sig/httpx.rbs +4 -1
  55. data/sig/loggable.rbs +3 -1
  56. data/sig/options.rbs +35 -32
  57. data/sig/plugins/authentication.rbs +1 -1
  58. data/sig/plugins/aws_sdk_authentication.rbs +5 -1
  59. data/sig/plugins/aws_sigv4.rbs +1 -2
  60. data/sig/plugins/basic_authentication.rbs +1 -1
  61. data/sig/plugins/compression.rbs +4 -6
  62. data/sig/plugins/cookies.rbs +4 -5
  63. data/sig/plugins/cookies/cookie.rbs +5 -7
  64. data/sig/plugins/cookies/jar.rbs +9 -10
  65. data/sig/plugins/digest_authentication.rbs +2 -3
  66. data/sig/plugins/expect.rbs +2 -4
  67. data/sig/plugins/follow_redirects.rbs +3 -5
  68. data/sig/plugins/grpc.rbs +4 -7
  69. data/sig/plugins/h2c.rbs +0 -2
  70. data/sig/plugins/multipart.rbs +2 -4
  71. data/sig/plugins/ntlm_authentication.rbs +2 -3
  72. data/sig/plugins/persistent.rbs +3 -8
  73. data/sig/plugins/proxy.rbs +7 -7
  74. data/sig/plugins/proxy/ssh.rbs +4 -4
  75. data/sig/plugins/push_promise.rbs +0 -2
  76. data/sig/plugins/retries.rbs +4 -8
  77. data/sig/plugins/stream.rbs +1 -1
  78. data/sig/plugins/upgrade.rbs +2 -3
  79. data/sig/pool.rbs +1 -2
  80. data/sig/registry.rbs +1 -1
  81. data/sig/request.rbs +2 -2
  82. data/sig/resolver.rbs +7 -0
  83. data/sig/resolver/native.rbs +9 -5
  84. data/sig/resolver/resolver_mixin.rbs +4 -5
  85. data/sig/resolver/system.rbs +2 -0
  86. data/sig/response.rbs +17 -11
  87. data/sig/selector.rbs +7 -6
  88. data/sig/session.rbs +19 -14
  89. data/sig/transcoder.rbs +11 -4
  90. data/sig/transcoder/body.rbs +6 -1
  91. data/sig/transcoder/chunker.rbs +8 -2
  92. data/sig/transcoder/form.rbs +2 -1
  93. data/sig/transcoder/json.rbs +1 -0
  94. data/sig/utils.rbs +2 -0
  95. metadata +12 -2
@@ -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
@@ -141,22 +141,23 @@ module HTTPX
141
141
  end
142
142
 
143
143
  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)
144
+ def load_dependencies(*)
153
145
  require "digest/sha2"
154
146
  require "openssl"
147
+ end
148
+
149
+ def configure(klass)
155
150
  klass.plugin(:expect)
156
151
  klass.plugin(:compression)
157
152
  end
158
153
  end
159
154
 
155
+ module OptionsMethods
156
+ def option_sigv4_signer(value)
157
+ value.is_a?(Signer) ? value : Signer.new(value)
158
+ end
159
+ end
160
+
160
161
  module InstanceMethods
161
162
  def aws_sigv4_authentication(**options)
162
163
  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
 
@@ -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
@@ -43,8 +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? ||
47
- @value <=> other.value
46
+ (@created_at <=> other.created_at).nonzero? || 0
48
47
  end
49
48
 
50
49
  class << self
@@ -108,6 +107,8 @@ module HTTPX
108
107
 
109
108
  @path ||= "/"
110
109
  raise ArgumentError, "name must be specified" if @name.nil?
110
+
111
+ @name = @name.to_s
111
112
  end
112
113
 
113
114
  def expires
@@ -123,7 +124,7 @@ module HTTPX
123
124
  # Returns a string for use in the Cookie header, i.e. `name=value`
124
125
  # or `name="value"`.
125
126
  def cookie_value
126
- "#{@name}=#{Scanner.quote(@value)}"
127
+ "#{@name}=#{Scanner.quote(@value.to_s)}"
127
128
  end
128
129
  alias_method :to_s, :cookie_value
129
130
 
@@ -43,6 +43,10 @@ module HTTPX
43
43
 
44
44
  c.path = path if path && c.path == "/"
45
45
 
46
+ # If the user agent receives a new cookie with the same cookie-name, domain-value, and path-value
47
+ # as a cookie that it has already stored, the existing cookie is evicted and replaced with the new cookie.
48
+ @cookies.delete_if { |ck| ck.name == c.name && ck.domain == c.domain && ck.path == c.path }
49
+
46
50
  @cookies << c
47
51
  end
48
52
 
@@ -53,7 +57,7 @@ module HTTPX
53
57
  def each(uri = nil, &blk)
54
58
  return enum_for(__method__, uri) unless block_given?
55
59
 
56
- return @store.each(&blk) unless uri
60
+ return @cookies.each(&blk) unless uri
57
61
 
58
62
  uri = URI(uri)
59
63
 
@@ -69,6 +73,25 @@ module HTTPX
69
73
  end
70
74
  end
71
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
72
95
  end
73
96
  end
74
97
  end
@@ -14,19 +14,23 @@ module HTTPX
14
14
 
15
15
  DigestError = Class.new(Error)
16
16
 
17
- def self.extra_options(options)
18
- Class.new(options.class) do
19
- def_option(:digest, <<-OUT)
20
- raise Error, ":digest must be a Digest" unless value.is_a?(#{Digest})
21
-
22
- value
23
- OUT
24
- end.new(options).merge(max_concurrent_requests: 1)
17
+ class << self
18
+ def extra_options(options)
19
+ options.merge(max_concurrent_requests: 1)
20
+ end
21
+
22
+ def load_dependencies(*)
23
+ require "securerandom"
24
+ require "digest"
25
+ end
25
26
  end
26
27
 
27
- def self.load_dependencies(*)
28
- require "securerandom"
29
- require "digest"
28
+ module OptionsMethods
29
+ def option_digest(value)
30
+ raise TypeError, ":digest must be a Digest" unless value.is_a?(Digest)
31
+
32
+ value
33
+ end
30
34
  end
31
35
 
32
36
  module InstanceMethods
@@ -10,26 +10,30 @@ module HTTPX
10
10
  module Expect
11
11
  EXPECT_TIMEOUT = 2
12
12
 
13
- def self.no_expect_store
14
- @no_expect_store ||= []
13
+ class << self
14
+ def no_expect_store
15
+ @no_expect_store ||= []
16
+ end
17
+
18
+ def extra_options(options)
19
+ options.merge(expect_timeout: EXPECT_TIMEOUT)
20
+ end
15
21
  end
16
22
 
17
- def self.extra_options(options)
18
- Class.new(options.class) do
19
- def_option(:expect_timeout, <<-OUT)
20
- seconds = Integer(value)
21
- raise Error, ":expect_timeout must be positive" unless seconds.positive?
23
+ module OptionsMethods
24
+ def option_expect_timeout(value)
25
+ seconds = Integer(value)
26
+ raise TypeError, ":expect_timeout must be positive" unless seconds.positive?
22
27
 
23
- seconds
24
- OUT
28
+ seconds
29
+ end
25
30
 
26
- def_option(:expect_threshold_size, <<-OUT)
27
- bytes = Integer(value)
28
- raise Error, ":expect_threshold_size must be positive" unless bytes.positive?
31
+ def option_expect_threshold_size(value)
32
+ bytes = Integer(value)
33
+ raise TypeError, ":expect_threshold_size must be positive" unless bytes.positive?
29
34
 
30
- bytes
31
- OUT
32
- end.new(options).merge(expect_timeout: EXPECT_TIMEOUT)
35
+ bytes
36
+ end
33
37
  end
34
38
 
35
39
  module RequestMethods
@@ -17,17 +17,17 @@ module HTTPX
17
17
  MAX_REDIRECTS = 3
18
18
  REDIRECT_STATUS = (300..399).freeze
19
19
 
20
- def self.extra_options(options)
21
- Class.new(options.class) do
22
- def_option(:max_redirects, <<-OUT)
23
- num = Integer(value)
24
- raise Error, ":max_redirects must be positive" if num.negative?
20
+ module OptionsMethods
21
+ def option_max_redirects(value)
22
+ num = Integer(value)
23
+ raise TypeError, ":max_redirects must be positive" if num.negative?
25
24
 
26
- num
27
- OUT
25
+ num
26
+ end
28
27
 
29
- def_option(:follow_insecure_redirects)
30
- end.new(options)
28
+ def option_follow_insecure_redirects(value)
29
+ value
30
+ end
31
31
  end
32
32
 
33
33
  module InstanceMethods
@@ -49,7 +49,6 @@ module HTTPX
49
49
  class << self
50
50
  def load_dependencies(*)
51
51
  require "stringio"
52
- require "google/protobuf"
53
52
  require "httpx/plugins/grpc/message"
54
53
  require "httpx/plugins/grpc/call"
55
54
  end
@@ -61,36 +60,7 @@ module HTTPX
61
60
  end
62
61
 
63
62
  def extra_options(options)
64
- Class.new(options.class) do
65
- def_option(:grpc_service, <<-OUT)
66
- String(value)
67
- OUT
68
-
69
- def_option(:grpc_compression, <<-OUT)
70
- case value
71
- when true, false
72
- value
73
- else
74
- value.to_s
75
- end
76
- OUT
77
-
78
- def_option(:grpc_rpcs, <<-OUT)
79
- Hash[value]
80
- OUT
81
-
82
- def_option(:grpc_deadline, <<-OUT)
83
- raise Error, ":grpc_deadline must be positive" unless value.positive?
84
-
85
- value
86
- OUT
87
-
88
- def_option(:call_credentials, <<-OUT)
89
- raise Error, ":call_credentials must respond to #call" unless value.respond_to?(:call)
90
-
91
- value
92
- OUT
93
- end.new(options).merge(
63
+ options.merge(
94
64
  fallback_protocol: "h2",
95
65
  http2_settings: { wait_for_handshake: false },
96
66
  grpc_rpcs: {}.freeze,
@@ -100,6 +70,37 @@ module HTTPX
100
70
  end
101
71
  end
102
72
 
73
+ module OptionsMethods
74
+ def option_grpc_service(value)
75
+ String(value)
76
+ end
77
+
78
+ def option_grpc_compression(value)
79
+ case value
80
+ when true, false
81
+ value
82
+ else
83
+ value.to_s
84
+ end
85
+ end
86
+
87
+ def option_grpc_rpcs(value)
88
+ Hash[value]
89
+ end
90
+
91
+ def option_grpc_deadline(value)
92
+ raise TypeError, ":grpc_deadline must be positive" unless value.positive?
93
+
94
+ value
95
+ end
96
+
97
+ def option_call_credentials(value)
98
+ raise TypeError, ":call_credentials must respond to #call" unless value.respond_to?(:call)
99
+
100
+ value
101
+ end
102
+ end
103
+
103
104
  module ResponseMethods
104
105
  attr_reader :trailing_metadata
105
106
 
@@ -140,9 +141,19 @@ module HTTPX
140
141
  deadline: @options.grpc_deadline,
141
142
  }.merge(opts)
142
143
 
143
- with(grpc_rpcs: @options.grpc_rpcs.merge(
144
- rpc_name.underscore => [rpc_name, input, output, rpc_opts]
145
- ).freeze)
144
+ session_class = Class.new(self.class) do
145
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
146
+ def #{rpc_name}(input, **opts)
147
+ rpc_execute("#{rpc_name}", input, **opts)
148
+ end
149
+ OUT
150
+ end
151
+
152
+ session_class.new(@options.merge(
153
+ grpc_rpcs: @options.grpc_rpcs.merge(
154
+ rpc_name.underscore => [rpc_name, input, output, rpc_opts]
155
+ ).freeze
156
+ ))
146
157
  end
147
158
 
148
159
  def build_stub(origin, service: nil, compression: false)
@@ -150,7 +161,32 @@ module HTTPX
150
161
 
151
162
  origin = URI.parse("#{scheme}://#{origin}")
152
163
 
153
- with(origin: origin, grpc_service: service, grpc_compression: compression)
164
+ session = self
165
+
166
+ if service && service.respond_to?(:rpc_descs)
167
+ # it's a grpc generic service
168
+ service.rpc_descs.each do |rpc_name, rpc_desc|
169
+ rpc_opts = {
170
+ marshal_method: rpc_desc.marshal_method,
171
+ unmarshal_method: rpc_desc.unmarshal_method,
172
+ }
173
+
174
+ input = rpc_desc.input
175
+ input = input.type if input.respond_to?(:type)
176
+
177
+ output = rpc_desc.output
178
+ if output.respond_to?(:type)
179
+ rpc_opts[:stream] = true
180
+ output = output.type
181
+ end
182
+
183
+ session = session.rpc(rpc_name, input, output, **rpc_opts)
184
+ end
185
+
186
+ service = service.service_name
187
+ end
188
+
189
+ session.with(origin: origin, grpc_service: service, grpc_compression: compression)
154
190
  end
155
191
 
156
192
  def execute(rpc_method, input,
@@ -166,7 +202,7 @@ module HTTPX
166
202
  private
167
203
 
168
204
  def rpc_execute(rpc_name, input, **opts)
169
- rpc_name, input_enc, output_enc, rpc_opts = @options.grpc_rpcs[rpc_name.to_s] || raise(Error, "#{rpc_name}: undefined service")
205
+ rpc_name, input_enc, output_enc, rpc_opts = @options.grpc_rpcs[rpc_name]
170
206
 
171
207
  exec_opts = rpc_opts.merge(opts)
172
208
 
@@ -180,7 +216,7 @@ module HTTPX
180
216
  end
181
217
  end
182
218
  else
183
- input_enc.marshal(input)
219
+ input_enc.__send__(marshal_method, input)
184
220
  end
185
221
 
186
222
  call = execute(rpc_name, messages, **exec_opts)
@@ -230,16 +266,6 @@ module HTTPX
230
266
 
231
267
  build_request(:post, uri, headers: headers, body: body)
232
268
  end
233
-
234
- def respond_to_missing?(meth, *, &blk)
235
- @options.grpc_rpcs.key?(meth.to_s) || super
236
- end
237
-
238
- def method_missing(meth, *args, **kwargs, &blk)
239
- return rpc_execute(meth, *args, **kwargs, &blk) if @options.grpc_rpcs.key?(meth.to_s)
240
-
241
- super
242
- end
243
269
  end
244
270
  end
245
271
  register_plugin :grpc, GRPC