httpx 0.24.6 → 1.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +0 -48
  3. data/README.md +4 -13
  4. data/doc/release_notes/0_24_4.md +3 -3
  5. data/doc/release_notes/1_0_0.md +60 -0
  6. data/lib/httpx/adapters/datadog.rb +28 -97
  7. data/lib/httpx/adapters/faraday.rb +9 -52
  8. data/lib/httpx/adapters/webmock.rb +2 -7
  9. data/lib/httpx/altsvc.rb +4 -22
  10. data/lib/httpx/base64.rb +27 -0
  11. data/lib/httpx/chainable.rb +0 -23
  12. data/lib/httpx/connection.rb +11 -32
  13. data/lib/httpx/domain_name.rb +5 -12
  14. data/lib/httpx/errors.rb +0 -2
  15. data/lib/httpx/extensions.rb +0 -124
  16. data/lib/httpx/io/ssl.rb +26 -59
  17. data/lib/httpx/io/tcp.rb +27 -68
  18. data/lib/httpx/io/udp.rb +13 -48
  19. data/lib/httpx/io/unix.rb +1 -8
  20. data/lib/httpx/loggable.rb +4 -19
  21. data/lib/httpx/options.rb +24 -84
  22. data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
  23. data/lib/httpx/plugins/{authentication → auth}/digest.rb +2 -5
  24. data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
  25. data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
  26. data/lib/httpx/plugins/auth.rb +25 -0
  27. data/lib/httpx/plugins/aws_sigv4.rb +0 -1
  28. data/lib/httpx/plugins/{basic_authentication.rb → basic_auth.rb} +5 -6
  29. data/lib/httpx/plugins/brotli.rb +50 -0
  30. data/lib/httpx/plugins/circuit_breaker/circuit.rb +40 -16
  31. data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +16 -5
  32. data/lib/httpx/plugins/circuit_breaker.rb +11 -4
  33. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
  34. data/lib/httpx/plugins/cookies.rb +1 -1
  35. data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +5 -5
  36. data/lib/httpx/plugins/follow_redirects.rb +21 -24
  37. data/lib/httpx/plugins/grpc/grpc_encoding.rb +82 -0
  38. data/lib/httpx/plugins/grpc/message.rb +7 -39
  39. data/lib/httpx/plugins/grpc.rb +15 -21
  40. data/lib/httpx/plugins/h2c.rb +0 -1
  41. data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +5 -5
  42. data/lib/httpx/plugins/oauth.rb +2 -2
  43. data/lib/httpx/plugins/proxy/http.rb +0 -2
  44. data/lib/httpx/plugins/proxy/socks4.rb +0 -4
  45. data/lib/httpx/plugins/proxy/socks5.rb +1 -5
  46. data/lib/httpx/plugins/proxy.rb +3 -32
  47. data/lib/httpx/plugins/retries.rb +3 -4
  48. data/lib/httpx/plugins/stream.rb +4 -6
  49. data/lib/httpx/punycode.rb +9 -291
  50. data/lib/httpx/request/body.rb +145 -0
  51. data/lib/httpx/request.rb +2 -119
  52. data/lib/httpx/resolver/https.rb +1 -1
  53. data/lib/httpx/resolver/native.rb +6 -14
  54. data/lib/httpx/resolver/resolver.rb +1 -1
  55. data/lib/httpx/resolver/system.rb +11 -9
  56. data/lib/httpx/response/body.rb +206 -0
  57. data/lib/httpx/response/buffer.rb +90 -0
  58. data/lib/httpx/response.rb +5 -208
  59. data/lib/httpx/selector.rb +0 -2
  60. data/lib/httpx/session.rb +0 -10
  61. data/lib/httpx/transcoder/body.rb +0 -1
  62. data/lib/httpx/transcoder/deflate.rb +37 -0
  63. data/lib/httpx/transcoder/form.rb +52 -32
  64. data/lib/httpx/transcoder/gzip.rb +74 -0
  65. data/lib/httpx/transcoder/json.rb +2 -4
  66. data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
  67. data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +3 -3
  68. data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
  69. data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
  70. data/lib/httpx/transcoder/multipart.rb +17 -0
  71. data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
  72. data/lib/httpx/transcoder/utils/deflater.rb +72 -0
  73. data/lib/httpx/transcoder/xml.rb +0 -2
  74. data/lib/httpx/transcoder.rb +2 -2
  75. data/lib/httpx/utils.rb +10 -20
  76. data/lib/httpx/version.rb +1 -1
  77. data/lib/httpx.rb +0 -8
  78. data/sig/chainable.rbs +5 -6
  79. data/sig/connection.rbs +0 -1
  80. data/sig/errors.rbs +0 -3
  81. data/sig/httpx.rbs +2 -1
  82. data/sig/io/unix.rbs +1 -1
  83. data/sig/options.rbs +12 -8
  84. data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
  85. data/sig/plugins/auth.rbs +13 -0
  86. data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
  87. data/sig/plugins/brotli.rbs +22 -0
  88. data/sig/plugins/circuit_breaker.rbs +7 -3
  89. data/sig/plugins/compression.rbs +0 -2
  90. data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
  91. data/sig/plugins/follow_redirects.rbs +0 -1
  92. data/sig/plugins/grpc/call.rbs +19 -0
  93. data/sig/plugins/grpc/grpc_encoding.rbs +33 -0
  94. data/sig/plugins/grpc/message.rbs +17 -0
  95. data/sig/plugins/grpc.rbs +2 -32
  96. data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
  97. data/sig/plugins/oauth.rbs +1 -1
  98. data/sig/plugins/proxy/socks4.rbs +2 -3
  99. data/sig/plugins/proxy/socks5.rbs +0 -1
  100. data/sig/plugins/proxy/ssh.rbs +1 -1
  101. data/sig/plugins/response_cache.rbs +5 -2
  102. data/sig/request/body.rbs +42 -0
  103. data/sig/request.rbs +1 -27
  104. data/sig/resolver/resolver.rbs +1 -1
  105. data/sig/response/body.rbs +52 -0
  106. data/sig/response/buffer.rbs +24 -0
  107. data/sig/response.rbs +0 -39
  108. data/sig/transcoder/body.rbs +4 -3
  109. data/sig/transcoder/deflate.rbs +11 -0
  110. data/sig/transcoder/form.rbs +5 -3
  111. data/sig/transcoder/gzip.rbs +24 -0
  112. data/sig/transcoder/json.rbs +4 -2
  113. data/sig/{plugins → transcoder}/multipart.rbs +3 -10
  114. data/sig/transcoder/utils/body_reader.rbs +15 -0
  115. data/sig/transcoder/utils/deflater.rbs +29 -0
  116. data/sig/transcoder.rbs +18 -2
  117. metadata +50 -34
  118. data/lib/httpx/plugins/authentication.rb +0 -24
  119. data/lib/httpx/plugins/compression/brotli.rb +0 -54
  120. data/lib/httpx/plugins/compression/deflate.rb +0 -54
  121. data/lib/httpx/plugins/compression/gzip.rb +0 -90
  122. data/lib/httpx/plugins/compression.rb +0 -165
  123. data/lib/httpx/plugins/multipart/decoder.rb +0 -137
  124. data/lib/httpx/plugins/multipart.rb +0 -96
  125. data/sig/plugins/authentication.rbs +0 -13
  126. data/sig/plugins/compression/brotli.rbs +0 -21
  127. data/sig/plugins/compression/deflate.rbs +0 -17
  128. data/sig/plugins/compression/gzip.rbs +0 -29
  129. /data/sig/plugins/{authentication → auth}/digest.rbs +0 -0
  130. /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
  131. /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Transcoder
5
+ module GRPCEncoding
6
+ class Deflater
7
+ extend Forwardable
8
+
9
+ attr_reader :content_type
10
+
11
+ def initialize(body, compressed:)
12
+ @content_type = body.content_type
13
+ @body = BodyReader.new(body)
14
+ @compressed = compressed
15
+ end
16
+
17
+ def bytesize
18
+ return @body.bytesize if @body.respond_to?(:bytesize)
19
+
20
+ Float::INFINITY
21
+ end
22
+
23
+ def read(length = nil, outbuf = nil)
24
+ buf = @body.read(length, outbuf)
25
+
26
+ return unless buf
27
+
28
+ compressed_flag = @compressed ? 1 : 0
29
+
30
+ buf = outbuf if outbuf
31
+
32
+ buf.prepend([compressed_flag, buf.bytesize].pack("CL>"))
33
+ buf
34
+ end
35
+ end
36
+
37
+ class Inflater
38
+ def initialize(response)
39
+ @encodings = response.headers.get("grpc-encoding")
40
+ @response = response
41
+ end
42
+
43
+ def call(message, &blk)
44
+ data = "".b
45
+
46
+ until message.empty?
47
+ compressed, size = message.unpack("CL>")
48
+
49
+ encoded_data = message.byteslice(5..size + 5 - 1)
50
+
51
+ if compressed == 1
52
+ @encodings.reverse_each do |encoding|
53
+ decoder = @response.body.class.initialize_inflater_by_encoding(encoding, @response, bytesize: encoded_data.bytesize)
54
+ encoded_data = decoder.call(encoded_data)
55
+
56
+ blk.call(encoded_data) if blk
57
+
58
+ data << encoded_data
59
+ end
60
+ else
61
+ blk.call(encoded_data) if blk
62
+
63
+ data << encoded_data
64
+ end
65
+
66
+ message = message.byteslice((size + 5)..-1)
67
+ end
68
+
69
+ data
70
+ end
71
+ end
72
+
73
+ def self.encode(*args, **kwargs)
74
+ Deflater.new(*args, **kwargs)
75
+ end
76
+
77
+ def self.decode(response)
78
+ Inflater.new(response)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -12,57 +12,25 @@ module HTTPX
12
12
  # decodes a unary grpc response
13
13
  def unary(response)
14
14
  verify_status(response)
15
- decode(response.to_s, encodings: response.headers.get("grpc-encoding"), encoders: response.encoders)
15
+
16
+ decoder = Transcoder::GRPCEncoding.decode(response)
17
+
18
+ decoder.call(response.to_s)
16
19
  end
17
20
 
18
21
  # lazy decodes a grpc stream response
19
22
  def stream(response, &block)
20
23
  return enum_for(__method__, response) unless block
21
24
 
25
+ decoder = Transcoder::GRPCEncoding.decode(response)
26
+
22
27
  response.each do |frame|
23
- decode(frame, encodings: response.headers.get("grpc-encoding"), encoders: response.encoders, &block)
28
+ decoder.call(frame, &block)
24
29
  end
25
30
 
26
31
  verify_status(response)
27
32
  end
28
33
 
29
- # encodes a single grpc message
30
- def encode(bytes, deflater:)
31
- if deflater
32
- compressed_flag = 1
33
- bytes = deflater.deflate(StringIO.new(bytes))
34
- else
35
- compressed_flag = 0
36
- end
37
-
38
- "".b << [compressed_flag, bytes.bytesize].pack("CL>") << bytes.to_s
39
- end
40
-
41
- # decodes a single grpc message
42
- def decode(message, encodings:, encoders:)
43
- until message.empty?
44
-
45
- compressed, size = message.unpack("CL>")
46
-
47
- data = message.byteslice(5..size + 5 - 1)
48
- if compressed == 1
49
- encodings.reverse_each do |algo|
50
- next unless encoders.key?(algo)
51
-
52
- inflater = encoders[algo].inflater(size)
53
- data = inflater.inflate(data)
54
- size = data.bytesize
55
- end
56
- end
57
-
58
- return data unless block_given?
59
-
60
- yield data
61
-
62
- message = message.byteslice((size + 5)..-1)
63
- end
64
- end
65
-
66
34
  def cancel(request)
67
35
  request.emit(:refuse, :client_cancellation)
68
36
  end
@@ -49,20 +49,19 @@ module HTTPX
49
49
  class << self
50
50
  def load_dependencies(*)
51
51
  require "stringio"
52
+ require "httpx/plugins/grpc/grpc_encoding"
52
53
  require "httpx/plugins/grpc/message"
53
54
  require "httpx/plugins/grpc/call"
54
55
  end
55
56
 
56
57
  def configure(klass)
57
58
  klass.plugin(:persistent)
58
- klass.plugin(:compression)
59
59
  klass.plugin(:stream)
60
60
  end
61
61
 
62
62
  def extra_options(options)
63
63
  options.merge(
64
64
  fallback_protocol: "h2",
65
- http2_settings: { wait_for_handshake: false },
66
65
  grpc_rpcs: {}.freeze,
67
66
  grpc_compression: false,
68
67
  grpc_deadline: DEADLINE
@@ -108,9 +107,18 @@ module HTTPX
108
107
  @trailing_metadata = Hash[trailers]
109
108
  super
110
109
  end
110
+ end
111
+
112
+ module RequestBodyMethods
113
+ def initialize(headers, _)
114
+ super
111
115
 
112
- def encoders
113
- @options.encodings
116
+ if (compression = headers["grpc-encoding"])
117
+ deflater_body = self.class.initialize_deflater_body(@body, compression)
118
+ @body = Transcoder::GRPCEncoding.encode(deflater_body || @body, compressed: !deflater_body.nil?)
119
+ else
120
+ @body = Transcoder::GRPCEncoding.encode(@body, compressed: false)
121
+ end
114
122
  end
115
123
  end
116
124
 
@@ -233,7 +241,7 @@ module HTTPX
233
241
  uri.path = rpc_method
234
242
 
235
243
  headers = HEADERS.merge(
236
- "grpc-accept-encoding" => ["identity", *@options.encodings.keys]
244
+ "grpc-accept-encoding" => ["identity", *@options.supported_compression_formats]
237
245
  )
238
246
  unless deadline == Float::INFINITY
239
247
  # convert to milliseconds
@@ -244,27 +252,13 @@ module HTTPX
244
252
  headers = headers.merge(metadata) if metadata
245
253
 
246
254
  # prepare compressor
247
- deflater = nil
248
255
  compression = @options.grpc_compression == true ? "gzip" : @options.grpc_compression
249
256
 
250
- if compression
251
- headers["grpc-encoding"] = compression
252
- deflater = @options.encodings[compression].deflater if @options.encodings.key?(compression)
253
- end
257
+ headers["grpc-encoding"] = compression if compression
254
258
 
255
259
  headers.merge!(@options.call_credentials.call) if @options.call_credentials
256
260
 
257
- body = if input.respond_to?(:each)
258
- Enumerator.new do |y|
259
- input.each do |message|
260
- y << Message.encode(message, deflater: deflater)
261
- end
262
- end
263
- else
264
- Message.encode(input, deflater: deflater)
265
- end
266
-
267
- build_request("POST", uri, headers: headers, body: body)
261
+ build_request("POST", uri, headers: headers, body: input)
268
262
  end
269
263
  end
270
264
  end
@@ -13,7 +13,6 @@ module HTTPX
13
13
 
14
14
  class << self
15
15
  def load_dependencies(klass)
16
- require "base64"
17
16
  klass.plugin(:upgrade)
18
17
  end
19
18
 
@@ -3,12 +3,12 @@
3
3
  module HTTPX
4
4
  module Plugins
5
5
  #
6
- # https://gitlab.com/os85/httpx/wikis/Authentication#ntlm-authentication
6
+ # https://gitlab.com/os85/httpx/wikis/Authorization#ntlm-auth
7
7
  #
8
8
  module NTLMAuth
9
9
  class << self
10
10
  def load_dependencies(_klass)
11
- require_relative "authentication/ntlm"
11
+ require_relative "auth/ntlm"
12
12
  end
13
13
 
14
14
  def extra_options(options)
@@ -25,11 +25,11 @@ module HTTPX
25
25
  end
26
26
 
27
27
  module InstanceMethods
28
- def ntlm_authentication(user, password, domain = nil)
28
+ def ntlm_auth(user, password, domain = nil)
29
29
  with(ntlm: Authentication::Ntlm.new(user, password, domain: domain))
30
30
  end
31
31
 
32
- alias_method :ntlm_auth, :ntlm_authentication
32
+ private
33
33
 
34
34
  def send_requests(*requests)
35
35
  requests.flat_map do |request|
@@ -55,6 +55,6 @@ module HTTPX
55
55
  end
56
56
  end
57
57
  end
58
- register_plugin :ntlm_authentication, NTLMAuth
58
+ register_plugin :ntlm_auth, NTLMAuth
59
59
  end
60
60
  end
@@ -8,7 +8,7 @@ module HTTPX
8
8
  module OAuth
9
9
  class << self
10
10
  def load_dependencies(_klass)
11
- require_relative "authentication/basic"
11
+ require_relative "auth/basic"
12
12
  end
13
13
  end
14
14
 
@@ -106,7 +106,7 @@ module HTTPX
106
106
  end
107
107
 
108
108
  module InstanceMethods
109
- def oauth_authentication(**args)
109
+ def oauth_auth(**args)
110
110
  with(oauth_session: OAuthSession.new(**args))
111
111
  end
112
112
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "base64"
4
-
5
3
  module HTTPX
6
4
  module Plugins
7
5
  module Proxy
@@ -92,10 +92,6 @@ module HTTPX
92
92
  @options = Options.new(options)
93
93
  end
94
94
 
95
- def timeout
96
- @options.timeout[:operation_timeout]
97
- end
98
-
99
95
  def close; end
100
96
 
101
97
  def consume(*); end
@@ -20,7 +20,7 @@ module HTTPX
20
20
 
21
21
  class << self
22
22
  def load_dependencies(*)
23
- require_relative "../authentication/socks5"
23
+ require_relative "../auth/socks5"
24
24
  end
25
25
 
26
26
  def extra_options(options)
@@ -144,10 +144,6 @@ module HTTPX
144
144
  @options = Options.new(options)
145
145
  end
146
146
 
147
- def timeout
148
- @options.timeout[:operation_timeout]
149
- end
150
-
151
147
  def close; end
152
148
 
153
149
  def consume(*); end
@@ -28,35 +28,6 @@ module HTTPX
28
28
  def extra_options(options)
29
29
  options.merge(supported_proxy_protocols: [])
30
30
  end
31
-
32
- if URI::Generic.methods.include?(:use_proxy?)
33
- def use_proxy?(*args)
34
- URI::Generic.use_proxy?(*args)
35
- end
36
- else
37
- # https://github.com/ruby/uri/blob/ae07f956a4bea00b4f54a75bd40b8fa918103eed/lib/uri/generic.rb
38
- def use_proxy?(hostname, addr, port, no_proxy)
39
- hostname = hostname.downcase
40
- dothostname = ".#{hostname}"
41
- no_proxy.scan(/([^:,\s]+)(?::(\d+))?/) do |p_host, p_port|
42
- if !p_port || port == p_port.to_i
43
- if p_host.start_with?(".")
44
- return false if hostname.end_with?(p_host.downcase)
45
- else
46
- return false if dothostname.end_with?(".#{p_host.downcase}")
47
- end
48
- if addr
49
- begin
50
- return false if IPAddr.new(p_host).include?(addr)
51
- rescue IPAddr::InvalidAddressError
52
- next
53
- end
54
- end
55
- end
56
- end
57
- true
58
- end
59
- end
60
31
  end
61
32
 
62
33
  class Parameters
@@ -82,7 +53,7 @@ module HTTPX
82
53
 
83
54
  auth_scheme = scheme.to_s.capitalize
84
55
 
85
- require_relative "authentication/#{scheme}" unless defined?(Authentication) && Authentication.const_defined?(auth_scheme, false)
56
+ require_relative "auth/#{scheme}" unless defined?(Authentication) && Authentication.const_defined?(auth_scheme, false)
86
57
 
87
58
  @authenticator = Authentication.const_get(auth_scheme).new(@username, @password, **extra)
88
59
  end
@@ -162,8 +133,8 @@ module HTTPX
162
133
  no_proxy = proxy[:no_proxy]
163
134
  no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
164
135
 
165
- return super(request, connections, options.merge(proxy: nil)) unless Proxy.use_proxy?(uri.host, next_proxy.host,
166
- next_proxy.port, no_proxy)
136
+ return super(request, connections, options.merge(proxy: nil)) unless URI::Generic.use_proxy?(uri.host, next_proxy.host,
137
+ next_proxy.port, no_proxy)
167
138
  end
168
139
 
169
140
  proxy.merge(uri: next_proxy)
@@ -88,13 +88,12 @@ module HTTPX
88
88
  request.retries.positive? &&
89
89
  __repeatable_request?(request, options) &&
90
90
  (
91
- # rubocop:disable Style/MultilineTernaryOperator
92
- options.retry_on ?
93
- options.retry_on.call(response) :
94
91
  (
95
92
  response.is_a?(ErrorResponse) && __retryable_error?(response.error)
93
+ ) ||
94
+ (
95
+ options.retry_on && options.retry_on.call(response)
96
96
  )
97
- # rubocop:enable Style/MultilineTernaryOperator
98
97
  )
99
98
  __try_partial_retry(request, response)
100
99
  log { "failed to get response, #{request.retries} tries to go..." }
@@ -94,6 +94,10 @@ module HTTPX
94
94
  # https://gitlab.com/honeyryderchuck/httpx/wikis/Stream
95
95
  #
96
96
  module Stream
97
+ def self.extra_options(options)
98
+ options.merge(timeout: { read_timeout: Float::INFINITY, operation_timeout: 60 })
99
+ end
100
+
97
101
  module InstanceMethods
98
102
  def request(*args, stream: false, **options)
99
103
  return super(*args, **options) unless stream
@@ -139,12 +143,6 @@ module HTTPX
139
143
  super
140
144
  end
141
145
  end
142
-
143
- def self.const_missing(const_name)
144
- super unless const_name == :StreamResponse
145
- warn "DEPRECATION WARNING: the class #{self}::StreamResponse is deprecated. Use HTTPX::StreamResponse instead."
146
- HTTPX::StreamResponse
147
- end
148
146
  end
149
147
  register_plugin :stream, Stream
150
148
  end