httpx 0.20.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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)