httpx 0.20.0 → 1.3.1

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 (250) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +0 -48
  3. data/README.md +54 -45
  4. data/doc/release_notes/0_10_0.md +2 -2
  5. data/doc/release_notes/0_11_0.md +3 -5
  6. data/doc/release_notes/0_12_0.md +5 -5
  7. data/doc/release_notes/0_13_0.md +5 -5
  8. data/doc/release_notes/0_14_0.md +2 -2
  9. data/doc/release_notes/0_16_0.md +3 -3
  10. data/doc/release_notes/0_17_0.md +1 -1
  11. data/doc/release_notes/0_18_0.md +4 -4
  12. data/doc/release_notes/0_18_2.md +1 -1
  13. data/doc/release_notes/0_19_0.md +1 -1
  14. data/doc/release_notes/0_19_8.md +1 -1
  15. data/doc/release_notes/0_20_0.md +2 -2
  16. data/doc/release_notes/0_20_1.md +5 -0
  17. data/doc/release_notes/0_20_2.md +7 -0
  18. data/doc/release_notes/0_20_3.md +6 -0
  19. data/doc/release_notes/0_20_4.md +17 -0
  20. data/doc/release_notes/0_20_5.md +3 -0
  21. data/doc/release_notes/0_21_0.md +96 -0
  22. data/doc/release_notes/0_21_1.md +12 -0
  23. data/doc/release_notes/0_22_0.md +13 -0
  24. data/doc/release_notes/0_22_1.md +11 -0
  25. data/doc/release_notes/0_22_2.md +5 -0
  26. data/doc/release_notes/0_22_3.md +55 -0
  27. data/doc/release_notes/0_22_4.md +6 -0
  28. data/doc/release_notes/0_22_5.md +6 -0
  29. data/doc/release_notes/0_23_0.md +42 -0
  30. data/doc/release_notes/0_23_1.md +5 -0
  31. data/doc/release_notes/0_23_2.md +5 -0
  32. data/doc/release_notes/0_23_3.md +6 -0
  33. data/doc/release_notes/0_23_4.md +5 -0
  34. data/doc/release_notes/0_24_0.md +48 -0
  35. data/doc/release_notes/0_24_1.md +12 -0
  36. data/doc/release_notes/0_24_2.md +12 -0
  37. data/doc/release_notes/0_24_3.md +12 -0
  38. data/doc/release_notes/0_24_4.md +18 -0
  39. data/doc/release_notes/0_24_5.md +6 -0
  40. data/doc/release_notes/0_24_6.md +5 -0
  41. data/doc/release_notes/0_24_7.md +10 -0
  42. data/doc/release_notes/1_0_0.md +60 -0
  43. data/doc/release_notes/1_0_1.md +5 -0
  44. data/doc/release_notes/1_0_2.md +7 -0
  45. data/doc/release_notes/1_1_0.md +32 -0
  46. data/doc/release_notes/1_1_1.md +17 -0
  47. data/doc/release_notes/1_1_2.md +12 -0
  48. data/doc/release_notes/1_1_3.md +18 -0
  49. data/doc/release_notes/1_1_4.md +6 -0
  50. data/doc/release_notes/1_1_5.md +12 -0
  51. data/doc/release_notes/1_2_0.md +49 -0
  52. data/doc/release_notes/1_2_1.md +6 -0
  53. data/doc/release_notes/1_2_2.md +10 -0
  54. data/doc/release_notes/1_2_3.md +16 -0
  55. data/doc/release_notes/1_2_4.md +8 -0
  56. data/doc/release_notes/1_2_5.md +7 -0
  57. data/doc/release_notes/1_2_6.md +13 -0
  58. data/doc/release_notes/1_3_0.md +18 -0
  59. data/doc/release_notes/1_3_1.md +17 -0
  60. data/lib/httpx/adapters/datadog.rb +215 -122
  61. data/lib/httpx/adapters/faraday.rb +145 -107
  62. data/lib/httpx/adapters/sentry.rb +26 -7
  63. data/lib/httpx/adapters/webmock.rb +34 -18
  64. data/lib/httpx/altsvc.rb +63 -26
  65. data/lib/httpx/base64.rb +27 -0
  66. data/lib/httpx/buffer.rb +12 -0
  67. data/lib/httpx/callbacks.rb +5 -3
  68. data/lib/httpx/chainable.rb +54 -39
  69. data/lib/httpx/connection/http1.rb +75 -44
  70. data/lib/httpx/connection/http2.rb +31 -38
  71. data/lib/httpx/connection.rb +287 -117
  72. data/lib/httpx/domain_name.rb +10 -13
  73. data/lib/httpx/errors.rb +52 -2
  74. data/lib/httpx/extensions.rb +24 -131
  75. data/lib/httpx/io/ssl.rb +83 -77
  76. data/lib/httpx/io/tcp.rb +48 -71
  77. data/lib/httpx/io/udp.rb +18 -52
  78. data/lib/httpx/io/unix.rb +10 -15
  79. data/lib/httpx/io.rb +3 -9
  80. data/lib/httpx/loggable.rb +4 -19
  81. data/lib/httpx/options.rb +176 -118
  82. data/lib/httpx/parser/http1.rb +4 -0
  83. data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
  84. data/lib/httpx/plugins/{authentication → auth}/digest.rb +14 -14
  85. data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
  86. data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
  87. data/lib/httpx/plugins/auth.rb +25 -0
  88. data/lib/httpx/plugins/aws_sdk_authentication.rb +4 -3
  89. data/lib/httpx/plugins/aws_sigv4.rb +12 -9
  90. data/lib/httpx/plugins/basic_auth.rb +29 -0
  91. data/lib/httpx/plugins/brotli.rb +50 -0
  92. data/lib/httpx/plugins/callbacks.rb +91 -0
  93. data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
  94. data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
  95. data/lib/httpx/plugins/circuit_breaker.rb +148 -0
  96. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
  97. data/lib/httpx/plugins/cookies.rb +30 -17
  98. data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +14 -12
  99. data/lib/httpx/plugins/expect.rb +21 -14
  100. data/lib/httpx/plugins/follow_redirects.rb +140 -41
  101. data/lib/httpx/plugins/grpc/call.rb +2 -3
  102. data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
  103. data/lib/httpx/plugins/grpc/message.rb +7 -37
  104. data/lib/httpx/plugins/grpc.rb +36 -29
  105. data/lib/httpx/plugins/h2c.rb +26 -19
  106. data/lib/httpx/plugins/internal_telemetry.rb +16 -0
  107. data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
  108. data/lib/httpx/plugins/oauth.rb +175 -0
  109. data/lib/httpx/plugins/persistent.rb +1 -1
  110. data/lib/httpx/plugins/proxy/http.rb +23 -13
  111. data/lib/httpx/plugins/proxy/socks4.rb +9 -7
  112. data/lib/httpx/plugins/proxy/socks5.rb +11 -9
  113. data/lib/httpx/plugins/proxy.rb +80 -61
  114. data/lib/httpx/plugins/push_promise.rb +1 -1
  115. data/lib/httpx/plugins/rate_limiter.rb +5 -1
  116. data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
  117. data/lib/httpx/plugins/response_cache/store.rb +62 -25
  118. data/lib/httpx/plugins/response_cache.rb +105 -12
  119. data/lib/httpx/plugins/retries.rb +87 -17
  120. data/lib/httpx/plugins/ssrf_filter.rb +145 -0
  121. data/lib/httpx/plugins/stream.rb +27 -23
  122. data/lib/httpx/plugins/upgrade/h2.rb +4 -4
  123. data/lib/httpx/plugins/upgrade.rb +8 -10
  124. data/lib/httpx/plugins/webdav.rb +80 -0
  125. data/lib/httpx/pool/synch_pool.rb +93 -0
  126. data/lib/httpx/pool.rb +102 -27
  127. data/lib/httpx/punycode.rb +9 -291
  128. data/lib/httpx/request/body.rb +154 -0
  129. data/lib/httpx/request.rb +130 -146
  130. data/lib/httpx/resolver/https.rb +62 -27
  131. data/lib/httpx/resolver/multi.rb +9 -13
  132. data/lib/httpx/resolver/native.rb +192 -76
  133. data/lib/httpx/resolver/resolver.rb +34 -9
  134. data/lib/httpx/resolver/system.rb +16 -11
  135. data/lib/httpx/resolver.rb +38 -16
  136. data/lib/httpx/response/body.rb +242 -0
  137. data/lib/httpx/response/buffer.rb +96 -0
  138. data/lib/httpx/response.rb +159 -217
  139. data/lib/httpx/selector.rb +9 -4
  140. data/lib/httpx/session.rb +137 -89
  141. data/lib/httpx/session_extensions.rb +4 -1
  142. data/lib/httpx/timers.rb +34 -8
  143. data/lib/httpx/transcoder/body.rb +0 -2
  144. data/lib/httpx/transcoder/chunker.rb +0 -1
  145. data/lib/httpx/transcoder/deflate.rb +37 -0
  146. data/lib/httpx/transcoder/form.rb +52 -33
  147. data/lib/httpx/transcoder/gzip.rb +74 -0
  148. data/lib/httpx/transcoder/json.rb +21 -8
  149. data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
  150. data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +4 -4
  151. data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
  152. data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
  153. data/lib/httpx/transcoder/multipart.rb +17 -0
  154. data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
  155. data/lib/httpx/transcoder/utils/deflater.rb +72 -0
  156. data/lib/httpx/transcoder/utils/inflater.rb +19 -0
  157. data/lib/httpx/transcoder/xml.rb +52 -0
  158. data/lib/httpx/transcoder.rb +5 -6
  159. data/lib/httpx/utils.rb +36 -16
  160. data/lib/httpx/version.rb +1 -1
  161. data/lib/httpx.rb +12 -14
  162. data/sig/altsvc.rbs +33 -0
  163. data/sig/buffer.rbs +2 -1
  164. data/sig/callbacks.rbs +3 -3
  165. data/sig/chainable.rbs +11 -9
  166. data/sig/connection/http1.rbs +8 -7
  167. data/sig/connection/http2.rbs +19 -19
  168. data/sig/connection.rbs +64 -24
  169. data/sig/errors.rbs +22 -3
  170. data/sig/httpx.rbs +5 -4
  171. data/sig/io/ssl.rbs +27 -0
  172. data/sig/io/tcp.rbs +60 -0
  173. data/sig/io/udp.rbs +20 -0
  174. data/sig/io/unix.rbs +27 -0
  175. data/sig/io.rbs +6 -0
  176. data/sig/options.rbs +32 -22
  177. data/sig/parser/http1.rbs +1 -1
  178. data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
  179. data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
  180. data/sig/plugins/auth.rbs +13 -0
  181. data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
  182. data/sig/plugins/brotli.rbs +22 -0
  183. data/sig/plugins/callbacks.rbs +38 -0
  184. data/sig/plugins/circuit_breaker.rbs +71 -0
  185. data/sig/plugins/compression.rbs +7 -5
  186. data/sig/plugins/cookies/jar.rbs +2 -2
  187. data/sig/plugins/cookies.rbs +2 -0
  188. data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
  189. data/sig/plugins/follow_redirects.rbs +18 -4
  190. data/sig/plugins/grpc/call.rbs +19 -0
  191. data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
  192. data/sig/plugins/grpc/message.rbs +17 -0
  193. data/sig/plugins/grpc.rbs +7 -32
  194. data/sig/plugins/h2c.rbs +1 -1
  195. data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
  196. data/sig/plugins/oauth.rbs +54 -0
  197. data/sig/plugins/proxy/http.rbs +3 -0
  198. data/sig/plugins/proxy/socks4.rbs +9 -6
  199. data/sig/plugins/proxy/socks5.rbs +10 -6
  200. data/sig/plugins/proxy/ssh.rbs +1 -1
  201. data/sig/plugins/proxy.rbs +13 -5
  202. data/sig/plugins/push_promise.rbs +3 -3
  203. data/sig/plugins/rate_limiter.rbs +1 -1
  204. data/sig/plugins/response_cache.rbs +36 -7
  205. data/sig/plugins/retries.rbs +30 -8
  206. data/sig/plugins/stream.rbs +24 -17
  207. data/sig/plugins/upgrade.rbs +5 -3
  208. data/sig/pool.rbs +10 -7
  209. data/sig/request/body.rbs +38 -0
  210. data/sig/request.rbs +15 -24
  211. data/sig/resolver/https.rbs +8 -3
  212. data/sig/resolver/native.rbs +17 -4
  213. data/sig/resolver/resolver.rbs +8 -6
  214. data/sig/resolver/system.rbs +2 -0
  215. data/sig/resolver.rbs +9 -5
  216. data/sig/response/body.rbs +53 -0
  217. data/sig/response/buffer.rbs +24 -0
  218. data/sig/response.rbs +24 -39
  219. data/sig/selector.rbs +1 -1
  220. data/sig/session.rbs +29 -18
  221. data/sig/timers.rbs +18 -8
  222. data/sig/transcoder/body.rbs +4 -3
  223. data/sig/transcoder/deflate.rbs +11 -0
  224. data/sig/transcoder/form.rbs +5 -3
  225. data/sig/transcoder/gzip.rbs +24 -0
  226. data/sig/transcoder/json.rbs +8 -3
  227. data/sig/{plugins → transcoder}/multipart.rbs +15 -19
  228. data/sig/transcoder/utils/body_reader.rbs +15 -0
  229. data/sig/transcoder/utils/deflater.rbs +29 -0
  230. data/sig/transcoder/utils/inflater.rbs +12 -0
  231. data/sig/transcoder/xml.rbs +22 -0
  232. data/sig/transcoder.rbs +24 -9
  233. data/sig/utils.rbs +8 -2
  234. metadata +163 -41
  235. data/lib/httpx/plugins/authentication.rb +0 -20
  236. data/lib/httpx/plugins/basic_authentication.rb +0 -30
  237. data/lib/httpx/plugins/compression/brotli.rb +0 -54
  238. data/lib/httpx/plugins/compression/deflate.rb +0 -49
  239. data/lib/httpx/plugins/compression/gzip.rb +0 -88
  240. data/lib/httpx/plugins/compression.rb +0 -164
  241. data/lib/httpx/plugins/multipart/decoder.rb +0 -187
  242. data/lib/httpx/plugins/multipart.rb +0 -84
  243. data/lib/httpx/registry.rb +0 -85
  244. data/sig/plugins/authentication.rbs +0 -11
  245. data/sig/plugins/compression/brotli.rbs +0 -21
  246. data/sig/plugins/compression/deflate.rbs +0 -17
  247. data/sig/plugins/compression/gzip.rbs +0 -29
  248. data/sig/registry.rbs +0 -12
  249. /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
  250. /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
data/lib/httpx/options.rb CHANGED
@@ -3,18 +3,22 @@
3
3
  require "socket"
4
4
 
5
5
  module HTTPX
6
+ # Contains a set of options which are passed and shared across from session to its requests or
7
+ # responses.
6
8
  class Options
9
+ BUFFER_SIZE = 1 << 14
7
10
  WINDOW_SIZE = 1 << 14 # 16K
8
11
  MAX_BODY_THRESHOLD_SIZE = (1 << 10) * 112 # 112K
9
- CONNECT_TIMEOUT = 60
10
- OPERATION_TIMEOUT = 60
11
12
  KEEP_ALIVE_TIMEOUT = 20
12
13
  SETTINGS_TIMEOUT = 10
14
+ CLOSE_HANDSHAKE_TIMEOUT = 10
15
+ CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
16
+ REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
13
17
 
14
18
  # https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
15
19
  ip_address_families = begin
16
20
  list = Socket.ip_address_list
17
- if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
21
+ if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? && !a.ipv6_unique_local? }
18
22
  [Socket::AF_INET6, Socket::AF_INET]
19
23
  else
20
24
  [Socket::AF_INET]
@@ -24,29 +28,37 @@ module HTTPX
24
28
  end
25
29
 
26
30
  DEFAULT_OPTIONS = {
31
+ :max_requests => Float::INFINITY,
27
32
  :debug => ENV.key?("HTTPX_DEBUG") ? $stderr : nil,
28
33
  :debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
29
34
  :ssl => {},
30
35
  :http2_settings => { settings_enable_push: 0 },
31
36
  :fallback_protocol => "http/1.1",
37
+ :supported_compression_formats => %w[gzip deflate],
38
+ :decompress_response_body => true,
39
+ :compress_request_body => true,
32
40
  :timeout => {
33
41
  connect_timeout: CONNECT_TIMEOUT,
34
42
  settings_timeout: SETTINGS_TIMEOUT,
43
+ close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
35
44
  operation_timeout: OPERATION_TIMEOUT,
36
45
  keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
46
+ read_timeout: READ_TIMEOUT,
47
+ write_timeout: WRITE_TIMEOUT,
48
+ request_timeout: REQUEST_TIMEOUT,
37
49
  },
50
+ :headers_class => Class.new(Headers),
38
51
  :headers => {},
39
52
  :window_size => WINDOW_SIZE,
53
+ :buffer_size => BUFFER_SIZE,
40
54
  :body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
41
55
  :request_class => Class.new(Request),
42
56
  :response_class => Class.new(Response),
43
- :headers_class => Class.new(Headers),
44
57
  :request_body_class => Class.new(Request::Body),
45
58
  :response_body_class => Class.new(Response::Body),
46
59
  :connection_class => Class.new(Connection),
47
60
  :options_class => Class.new(self),
48
61
  :transport => nil,
49
- :transport_options => nil,
50
62
  :addresses => nil,
51
63
  :persistent => false,
52
64
  :resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
@@ -54,28 +66,6 @@ module HTTPX
54
66
  :ip_families => ip_address_families,
55
67
  }.freeze
56
68
 
57
- begin
58
- module HashExtensions
59
- refine Hash do
60
- def >=(other)
61
- Hash[other] <= self
62
- end
63
-
64
- def <=(other)
65
- other = Hash[other]
66
- return false unless size <= other.size
67
-
68
- each do |k, v|
69
- v2 = other.fetch(k) { return false }
70
- return false unless v2 == v
71
- end
72
- true
73
- end
74
- end
75
- end
76
- using HashExtensions
77
- end unless Hash.method_defined?(:>=)
78
-
79
69
  class << self
80
70
  def new(options = {})
81
71
  # let enhanced options go through
@@ -94,38 +84,50 @@ module HTTPX
94
84
 
95
85
  attr_reader(optname)
96
86
  end
97
-
98
- def def_option(optname, *args, &block)
99
- if args.size.zero? && !block
100
- class_eval(<<-OUT, __FILE__, __LINE__ + 1)
101
- def option_#{optname}(v); v; end # def option_smth(v); v; end
102
- OUT
103
- return
104
- end
105
-
106
- deprecated_def_option(optname, *args, &block)
107
- end
108
-
109
- def deprecated_def_option(optname, layout = nil, &interpreter)
110
- warn "DEPRECATION WARNING: using `def_option(#{optname})` for setting options is deprecated. " \
111
- "Define module OptionsMethods and `def option_#{optname}(val)` instead."
112
-
113
- if layout
114
- class_eval(<<-OUT, __FILE__, __LINE__ + 1)
115
- def option_#{optname}(value) # def option_origin(v)
116
- #{layout} # URI(v)
117
- end # end
118
- OUT
119
- elsif interpreter
120
- define_method(:"option_#{optname}") do |value|
121
- instance_exec(value, &interpreter)
122
- end
123
- end
124
- end
125
87
  end
126
88
 
89
+ # creates a new options instance from a given hash, which optionally define the following:
90
+ #
91
+ # :debug :: an object which log messages are written to (must respond to <tt><<</tt>)
92
+ # :debug_level :: the log level of messages (can be 1, 2, or 3).
93
+ # :ssl :: a hash of options which can be set as params of OpenSSL::SSL::SSLContext (see HTTPX::IO::SSL)
94
+ # :http2_settings :: a hash of options to be passed to a HTTP2::Connection (ex: <tt>{ max_concurrent_streams: 2 }</tt>)
95
+ # :fallback_protocol :: version of HTTP protocol to use by default in the absence of protocol negotiation
96
+ # like ALPN (defaults to <tt>"http/1.1"</tt>)
97
+ # :supported_compression_formats :: list of compressions supported by the transcoder layer (defaults to <tt>%w[gzip deflate]</tt>).
98
+ # :decompress_response_body :: whether to auto-decompress response body (defaults to <tt>true</tt>).
99
+ # :compress_request_body :: whether to auto-decompress response body (defaults to <tt>true</tt>)
100
+ # :timeout :: hash of timeout configurations (supports <tt>:connect_timeout</tt>, <tt>:settings_timeout</tt>,
101
+ # <tt>:operation_timeout</tt>, <tt>:keep_alive_timeout</tt>, <tt>:read_timeout</tt>, <tt>:write_timeout</tt>
102
+ # and <tt>:request_timeout</tt>
103
+ # :headers :: hash of HTTP headers (ex: <tt>{ "x-custom-foo" => "bar" }</tt>)
104
+ # :window_size :: number of bytes to read from a socket
105
+ # :buffer_size :: internal read and write buffer size in bytes
106
+ # :body_threshold_size :: maximum size in bytes of response payload that is buffered in memory.
107
+ # :request_class :: class used to instantiate a request
108
+ # :response_class :: class used to instantiate a response
109
+ # :headers_class :: class used to instantiate headers
110
+ # :request_body_class :: class used to instantiate a request body
111
+ # :response_body_class :: class used to instantiate a response body
112
+ # :connection_class :: class used to instantiate connections
113
+ # :options_class :: class used to instantiate options
114
+ # :transport :: type of transport to use (set to "unix" for UNIX sockets)
115
+ # :addresses :: bucket of peer addresses (can be a list of IP addresses, a hash of domain to list of adddresses;
116
+ # paths should be used for UNIX sockets instead)
117
+ # :io :: open socket, or domain/ip-to-socket hash, which requests should be sent to
118
+ # :persistent :: whether to persist connections in between requests (defaults to <tt>true</tt>)
119
+ # :resolver_class :: which resolver to use (defaults to <tt>:native</tt>, can also be <tt>:system<tt> for
120
+ # using getaddrinfo or <tt>:https</tt> for DoH resolver, or a custom class)
121
+ # :resolver_options :: hash of options passed to the resolver
122
+ # :ip_families :: which socket families are supported (system-dependent)
123
+ # :origin :: HTTP origin to set on requests with relative path (ex: "https://api.serv.com")
124
+ # :base_path :: path to prefix given relative paths with (ex: "/v2")
125
+ # :max_concurrent_requests :: max number of requests which can be set concurrently
126
+ # :max_requests :: max number of requests which can be made on socket before it reconnects.
127
+ #
128
+ # This list of options are enhanced with each loaded plugin, see the plugin docs for details.
127
129
  def initialize(options = {})
128
- __initialize__(options)
130
+ do_initialize(options)
129
131
  freeze
130
132
  end
131
133
 
@@ -136,6 +138,7 @@ module HTTPX
136
138
  @timeout.freeze
137
139
  @headers.freeze
138
140
  @addresses.freeze
141
+ @supported_compression_formats.freeze
139
142
  end
140
143
 
141
144
  def option_origin(value)
@@ -147,18 +150,15 @@ module HTTPX
147
150
  end
148
151
 
149
152
  def option_headers(value)
150
- Headers.new(value)
153
+ headers_class.new(value)
151
154
  end
152
155
 
153
156
  def option_timeout(value)
154
- timeouts = Hash[value]
155
-
156
- if timeouts.key?(:loop_timeout)
157
- warn ":loop_timeout is deprecated, use :operation_timeout instead"
158
- timeouts[:operation_timeout] = timeouts.delete(:loop_timeout)
159
- end
157
+ Hash[value]
158
+ end
160
159
 
161
- timeouts
160
+ def option_supported_compression_formats(value)
161
+ Array(value).map(&:to_s)
162
162
  end
163
163
 
164
164
  def option_max_concurrent_requests(value)
@@ -174,16 +174,31 @@ module HTTPX
174
174
  end
175
175
 
176
176
  def option_window_size(value)
177
- Integer(value)
177
+ value = Integer(value)
178
+
179
+ raise TypeError, ":window_size must be positive" unless value.positive?
180
+
181
+ value
182
+ end
183
+
184
+ def option_buffer_size(value)
185
+ value = Integer(value)
186
+
187
+ raise TypeError, ":buffer_size must be positive" unless value.positive?
188
+
189
+ value
178
190
  end
179
191
 
180
192
  def option_body_threshold_size(value)
181
- Integer(value)
193
+ bytes = Integer(value)
194
+ raise TypeError, ":body_threshold_size must be positive" unless bytes.positive?
195
+
196
+ bytes
182
197
  end
183
198
 
184
199
  def option_transport(value)
185
200
  transport = value.to_s
186
- raise TypeError, "\#{transport} is an unsupported transport type" unless IO.registry.key?(transport)
201
+ raise TypeError, "#{transport} is an unsupported transport type" unless %w[unix].include?(transport)
187
202
 
188
203
  transport
189
204
  end
@@ -197,53 +212,82 @@ module HTTPX
197
212
  end
198
213
 
199
214
  %i[
200
- params form json body ssl http2_settings
215
+ ssl http2_settings
201
216
  request_class response_class headers_class request_body_class
202
217
  response_body_class connection_class options_class
203
- io fallback_protocol debug debug_level transport_options resolver_class resolver_options
218
+ io fallback_protocol debug debug_level resolver_class resolver_options
219
+ compress_request_body decompress_response_body
204
220
  persistent
205
221
  ].each do |method_name|
206
- def_option(method_name)
222
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
223
+ # sets +v+ as the value of #{method_name}
224
+ def option_#{method_name}(v); v; end # def option_smth(v); v; end
225
+ OUT
207
226
  end
208
227
 
209
- REQUEST_IVARS = %i[@params @form @json @body].freeze
210
- private_constant :REQUEST_IVARS
228
+ REQUEST_BODY_IVARS = %i[@headers].freeze
211
229
 
212
230
  def ==(other)
213
- ivars = instance_variables | other.instance_variables
231
+ super || options_equals?(other)
232
+ end
233
+
234
+ def options_equals?(other, ignore_ivars = REQUEST_BODY_IVARS)
235
+ # headers and other request options do not play a role, as they are
236
+ # relevant only for the request.
237
+ ivars = instance_variables - ignore_ivars
238
+ other_ivars = other.instance_variables - ignore_ivars
239
+
240
+ return false if ivars.size != other_ivars.size
241
+
242
+ return false if ivars.sort != other_ivars.sort
243
+
214
244
  ivars.all? do |ivar|
215
- case ivar
216
- when :@headers
217
- # currently, this is used to pick up an available matching connection.
218
- # the headers do not play a role, as they are relevant only for the request.
219
- true
220
- when *REQUEST_IVARS
221
- true
222
- else
223
- instance_variable_get(ivar) == other.instance_variable_get(ivar)
224
- end
245
+ instance_variable_get(ivar) == other.instance_variable_get(ivar)
225
246
  end
226
247
  end
227
248
 
249
+ OTHER_LOOKUP = ->(obj, k, ivar_map) {
250
+ case obj
251
+ when Hash
252
+ obj[ivar_map[k]]
253
+ else
254
+ obj.instance_variable_get(k)
255
+ end
256
+ }
228
257
  def merge(other)
229
- raise ArgumentError, "#{other} is not a valid set of options" unless other.respond_to?(:to_hash)
258
+ ivar_map = nil
259
+ other_ivars = case other
260
+ when Hash
261
+ ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
262
+ ivar_map.keys
263
+ else
264
+ other.instance_variables
265
+ end
230
266
 
231
- h2 = other.to_hash
232
- return self if h2.empty?
267
+ return self if other_ivars.empty?
233
268
 
234
- h1 = to_hash
269
+ return self if other_ivars.all? { |ivar| instance_variable_get(ivar) == OTHER_LOOKUP[other, ivar, ivar_map] }
235
270
 
236
- return self if h1 >= h2
271
+ opts = dup
237
272
 
238
- merged = h1.merge(h2) do |_k, v1, v2|
239
- if v1.respond_to?(:merge) && v2.respond_to?(:merge)
240
- v1.merge(v2)
241
- else
242
- v2
273
+ other_ivars.each do |ivar|
274
+ v = OTHER_LOOKUP[other, ivar, ivar_map]
275
+
276
+ unless v
277
+ opts.instance_variable_set(ivar, v)
278
+ next
243
279
  end
280
+
281
+ v = opts.__send__(:"option_#{ivar[1..-1]}", v)
282
+
283
+ orig_v = instance_variable_get(ivar)
284
+
285
+ v = orig_v.merge(v) if orig_v.respond_to?(:merge) && v.respond_to?(:merge)
286
+
287
+ opts.instance_variable_set(ivar, v)
244
288
  end
245
289
 
246
- self.class.new(merged)
290
+ opts
247
291
  end
248
292
 
249
293
  def to_hash
@@ -252,40 +296,54 @@ module HTTPX
252
296
  end
253
297
  end
254
298
 
255
- if RUBY_VERSION > "2.4.0"
256
- def initialize_dup(other)
257
- instance_variables.each do |ivar|
258
- instance_variable_set(ivar, other.instance_variable_get(ivar).dup)
259
- end
299
+ def extend_with_plugin_classes(pl)
300
+ if defined?(pl::RequestMethods) || defined?(pl::RequestClassMethods)
301
+ @request_class = @request_class.dup
302
+ @request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
303
+ @request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
260
304
  end
261
- else
262
- def initialize_dup(other)
263
- instance_variables.each do |ivar|
264
- value = other.instance_variable_get(ivar)
265
- value = case value
266
- when Symbol, Fixnum, TrueClass, FalseClass # rubocop:disable Lint/UnifiedInteger
267
- value
268
- else
269
- value.dup
270
- end
271
- instance_variable_set(ivar, value)
272
- end
305
+ if defined?(pl::ResponseMethods) || defined?(pl::ResponseClassMethods)
306
+ @response_class = @response_class.dup
307
+ @response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
308
+ @response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
309
+ end
310
+ if defined?(pl::HeadersMethods) || defined?(pl::HeadersClassMethods)
311
+ @headers_class = @headers_class.dup
312
+ @headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
313
+ @headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
314
+ end
315
+ if defined?(pl::RequestBodyMethods) || defined?(pl::RequestBodyClassMethods)
316
+ @request_body_class = @request_body_class.dup
317
+ @request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
318
+ @request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
273
319
  end
320
+ if defined?(pl::ResponseBodyMethods) || defined?(pl::ResponseBodyClassMethods)
321
+ @response_body_class = @response_body_class.dup
322
+ @response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
323
+ @response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
324
+ end
325
+ if defined?(pl::ConnectionMethods)
326
+ @connection_class = @connection_class.dup
327
+ @connection_class.__send__(:include, pl::ConnectionMethods)
328
+ end
329
+ return unless defined?(pl::OptionsMethods)
330
+
331
+ @options_class = @options_class.dup
332
+ @options_class.__send__(:include, pl::OptionsMethods)
274
333
  end
275
334
 
276
335
  private
277
336
 
278
- def __initialize__(options = {})
337
+ def do_initialize(options = {})
279
338
  defaults = DEFAULT_OPTIONS.merge(options)
280
339
  defaults.each do |k, v|
281
340
  next if v.nil?
282
341
 
283
- begin
284
- value = __send__(:"option_#{k}", v)
285
- instance_variable_set(:"@#{k}", value)
286
- rescue NoMethodError
287
- raise Error, "unknown option: #{k}"
288
- end
342
+ option_method_name = :"option_#{k}"
343
+ raise Error, "unknown option: #{k}" unless respond_to?(option_method_name)
344
+
345
+ value = __send__(option_method_name, v)
346
+ instance_variable_set(:"@#{k}", value)
289
347
  end
290
348
  end
291
349
  end
@@ -75,6 +75,7 @@ module HTTPX
75
75
  buffer = @buffer
76
76
 
77
77
  while (idx = buffer.index("\n"))
78
+ # @type var line: String
78
79
  line = buffer.byteslice(0..idx)
79
80
  raise Error, "wrong header format" if line.start_with?("\s", "\t")
80
81
 
@@ -101,9 +102,11 @@ module HTTPX
101
102
  separator_index = line.index(":")
102
103
  raise Error, "wrong header format" unless separator_index
103
104
 
105
+ # @type var key: String
104
106
  key = line.byteslice(0..(separator_index - 1))
105
107
 
106
108
  key.rstrip! # was lstripped previously!
109
+ # @type var value: String
107
110
  value = line.byteslice((separator_index + 1)..-1)
108
111
  value.strip!
109
112
  raise Error, "wrong header format" if value.nil?
@@ -118,6 +121,7 @@ module HTTPX
118
121
  @observer.on_data(chunk)
119
122
  end
120
123
  elsif @content_length
124
+ # @type var data: String
121
125
  data = @buffer.byteslice(0, @content_length)
122
126
  @buffer = @buffer.byteslice(@content_length..-1) || "".b
123
127
  @content_length -= data.bytesize
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "base64"
3
+ require "httpx/base64"
4
4
 
5
5
  module HTTPX
6
6
  module Plugins
@@ -11,10 +11,6 @@ module HTTPX
11
11
  @password = password
12
12
  end
13
13
 
14
- def can_authenticate?(authenticate)
15
- authenticate && /Basic .*/.match?(authenticate)
16
- end
17
-
18
14
  def authenticate(*)
19
15
  "Basic #{Base64.strict_encode64("#{@user}:#{@password}")}"
20
16
  end
@@ -8,12 +8,11 @@ module HTTPX
8
8
  module Plugins
9
9
  module Authentication
10
10
  class Digest
11
- using RegexpExtensions unless Regexp.method_defined?(:match?)
12
-
13
- def initialize(user, password, **)
11
+ def initialize(user, password, hashed: false, **)
14
12
  @user = user
15
13
  @password = password
16
14
  @nonce = 0
15
+ @hashed = hashed
17
16
  end
18
17
 
19
18
  def can_authenticate?(authenticate)
@@ -21,7 +20,7 @@ module HTTPX
21
20
  end
22
21
 
23
22
  def authenticate(request, authenticate)
24
- "Digest #{generate_header(request.verb.to_s.upcase, request.path, authenticate)}"
23
+ "Digest #{generate_header(request.verb, request.path, authenticate)}"
25
24
  end
26
25
 
27
26
  private
@@ -30,9 +29,9 @@ module HTTPX
30
29
  # discard first token, it's Digest
31
30
  auth_info = authenticate[/^(\w+) (.*)/, 2]
32
31
 
33
- params = Hash[auth_info.split(/ *, */)
34
- .map { |val| val.split("=") }
35
- .map { |k, v| [k, v.delete("\"")] }]
32
+ params = auth_info.split(/ *, */)
33
+ .to_h { |val| val.split("=", 2) }
34
+ .transform_values { |v| v.delete("\"") }
36
35
  nonce = params["nonce"]
37
36
  nc = next_nonce
38
37
 
@@ -45,7 +44,6 @@ module HTTPX
45
44
  raise DigestError, "unknown algorithm \"#{alg}\"" unless algorithm
46
45
 
47
46
  sess = Regexp.last_match(2)
48
- params.delete("algorithm")
49
47
  else
50
48
  algorithm = ::Digest::MD5
51
49
  end
@@ -56,11 +54,13 @@ module HTTPX
56
54
  end
57
55
 
58
56
  a1 = if sess
59
- [algorithm.hexdigest("#{@user}:#{params["realm"]}:#{@password}"),
60
- nonce,
61
- cnonce].join ":"
57
+ [
58
+ (@hashed ? @password : algorithm.hexdigest("#{@user}:#{params["realm"]}:#{@password}")),
59
+ nonce,
60
+ cnonce,
61
+ ].join ":"
62
62
  else
63
- "#{@user}:#{params["realm"]}:#{@password}"
63
+ @hashed ? @password : "#{@user}:#{params["realm"]}:#{@password}"
64
64
  end
65
65
 
66
66
  ha1 = algorithm.hexdigest(a1)
@@ -77,11 +77,11 @@ module HTTPX
77
77
  %(response="#{algorithm.hexdigest(request_digest)}"),
78
78
  ]
79
79
  header << %(realm="#{params["realm"]}") if params.key?("realm")
80
- header << %(algorithm=#{params["algorithm"]}") if params.key?("algorithm")
81
- header << %(opaque="#{params["opaque"]}") if params.key?("opaque")
80
+ header << %(algorithm=#{params["algorithm"]}) if params.key?("algorithm")
82
81
  header << %(cnonce="#{cnonce}") if cnonce
83
82
  header << %(nc=#{nc})
84
83
  header << %(qop=#{qop}) if qop
84
+ header << %(opaque="#{params["opaque"]}") if params.key?("opaque")
85
85
  header.join ", "
86
86
  end
87
87
 
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "base64"
3
+ require "httpx/base64"
4
4
  require "ntlm"
5
5
 
6
6
  module HTTPX
7
7
  module Plugins
8
8
  module Authentication
9
9
  class Ntlm
10
- using RegexpExtensions unless Regexp.method_defined?(:match?)
11
-
12
10
  def initialize(user, password, domain: nil)
13
11
  @user = user
14
12
  @password = password
@@ -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 Authentication
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds a shim +authorization+ method to the session, which will fill
7
+ # the HTTP Authorization header, and another, +bearer_auth+, which fill the "Bearer " prefix
8
+ # in its value.
9
+ #
10
+ # https://gitlab.com/os85/httpx/wikis/Auth#auth
11
+ #
12
+ module Auth
13
+ module InstanceMethods
14
+ def authorization(token)
15
+ with(headers: { "authorization" => token })
16
+ end
17
+
18
+ def bearer_auth(token)
19
+ authorization("Bearer #{token}")
20
+ end
21
+ end
22
+ end
23
+ register_plugin :auth, Auth
24
+ end
25
+ end
@@ -20,9 +20,7 @@ module HTTPX
20
20
  true
21
21
  end
22
22
 
23
- def method_missing(*)
24
- nil
25
- end
23
+ def method_missing(*); end
26
24
  end
27
25
 
28
26
  #
@@ -74,6 +72,9 @@ module HTTPX
74
72
  end
75
73
  end
76
74
 
75
+ # adds support for the following options:
76
+ #
77
+ # :aws_profile :: AWS account profile to retrieve credentials from.
77
78
  module OptionsMethods
78
79
  def option_aws_profile(value)
79
80
  String(value)