httpx 0.21.0 → 1.2.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 (229) 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 +4 -4
  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_20_0.md +1 -1
  15. data/doc/release_notes/0_21_0.md +7 -5
  16. data/doc/release_notes/0_21_1.md +12 -0
  17. data/doc/release_notes/0_22_0.md +13 -0
  18. data/doc/release_notes/0_22_1.md +11 -0
  19. data/doc/release_notes/0_22_2.md +5 -0
  20. data/doc/release_notes/0_22_3.md +55 -0
  21. data/doc/release_notes/0_22_4.md +6 -0
  22. data/doc/release_notes/0_22_5.md +6 -0
  23. data/doc/release_notes/0_23_0.md +42 -0
  24. data/doc/release_notes/0_23_1.md +5 -0
  25. data/doc/release_notes/0_23_2.md +5 -0
  26. data/doc/release_notes/0_23_3.md +6 -0
  27. data/doc/release_notes/0_23_4.md +5 -0
  28. data/doc/release_notes/0_24_0.md +48 -0
  29. data/doc/release_notes/0_24_1.md +12 -0
  30. data/doc/release_notes/0_24_2.md +12 -0
  31. data/doc/release_notes/0_24_3.md +12 -0
  32. data/doc/release_notes/0_24_4.md +18 -0
  33. data/doc/release_notes/0_24_5.md +6 -0
  34. data/doc/release_notes/0_24_6.md +5 -0
  35. data/doc/release_notes/0_24_7.md +10 -0
  36. data/doc/release_notes/1_0_0.md +60 -0
  37. data/doc/release_notes/1_0_1.md +5 -0
  38. data/doc/release_notes/1_0_2.md +7 -0
  39. data/doc/release_notes/1_1_0.md +32 -0
  40. data/doc/release_notes/1_1_1.md +17 -0
  41. data/doc/release_notes/1_1_2.md +12 -0
  42. data/doc/release_notes/1_1_3.md +18 -0
  43. data/doc/release_notes/1_1_4.md +6 -0
  44. data/doc/release_notes/1_1_5.md +12 -0
  45. data/doc/release_notes/1_2_0.md +49 -0
  46. data/doc/release_notes/1_2_1.md +6 -0
  47. data/lib/httpx/adapters/datadog.rb +100 -106
  48. data/lib/httpx/adapters/faraday.rb +143 -107
  49. data/lib/httpx/adapters/sentry.rb +26 -7
  50. data/lib/httpx/adapters/webmock.rb +33 -17
  51. data/lib/httpx/altsvc.rb +61 -24
  52. data/lib/httpx/base64.rb +27 -0
  53. data/lib/httpx/buffer.rb +12 -0
  54. data/lib/httpx/callbacks.rb +5 -3
  55. data/lib/httpx/chainable.rb +54 -39
  56. data/lib/httpx/connection/http1.rb +62 -37
  57. data/lib/httpx/connection/http2.rb +16 -27
  58. data/lib/httpx/connection.rb +213 -120
  59. data/lib/httpx/domain_name.rb +10 -13
  60. data/lib/httpx/errors.rb +34 -2
  61. data/lib/httpx/extensions.rb +4 -134
  62. data/lib/httpx/io/ssl.rb +77 -71
  63. data/lib/httpx/io/tcp.rb +46 -70
  64. data/lib/httpx/io/udp.rb +18 -52
  65. data/lib/httpx/io/unix.rb +6 -13
  66. data/lib/httpx/io.rb +3 -9
  67. data/lib/httpx/loggable.rb +4 -19
  68. data/lib/httpx/options.rb +168 -110
  69. data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
  70. data/lib/httpx/plugins/{authentication → auth}/digest.rb +13 -14
  71. data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
  72. data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
  73. data/lib/httpx/plugins/auth.rb +25 -0
  74. data/lib/httpx/plugins/aws_sdk_authentication.rb +1 -3
  75. data/lib/httpx/plugins/aws_sigv4.rb +5 -6
  76. data/lib/httpx/plugins/basic_auth.rb +29 -0
  77. data/lib/httpx/plugins/brotli.rb +50 -0
  78. data/lib/httpx/plugins/callbacks.rb +91 -0
  79. data/lib/httpx/plugins/circuit_breaker/circuit.rb +40 -16
  80. data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +14 -5
  81. data/lib/httpx/plugins/circuit_breaker.rb +30 -7
  82. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
  83. data/lib/httpx/plugins/cookies.rb +20 -10
  84. data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +11 -12
  85. data/lib/httpx/plugins/expect.rb +15 -13
  86. data/lib/httpx/plugins/follow_redirects.rb +71 -29
  87. data/lib/httpx/plugins/grpc/call.rb +2 -3
  88. data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
  89. data/lib/httpx/plugins/grpc/message.rb +7 -37
  90. data/lib/httpx/plugins/grpc.rb +35 -29
  91. data/lib/httpx/plugins/h2c.rb +25 -18
  92. data/lib/httpx/plugins/internal_telemetry.rb +16 -0
  93. data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
  94. data/lib/httpx/plugins/oauth.rb +170 -0
  95. data/lib/httpx/plugins/persistent.rb +1 -1
  96. data/lib/httpx/plugins/proxy/http.rb +15 -10
  97. data/lib/httpx/plugins/proxy/socks4.rb +8 -6
  98. data/lib/httpx/plugins/proxy/socks5.rb +10 -8
  99. data/lib/httpx/plugins/proxy.rb +69 -67
  100. data/lib/httpx/plugins/push_promise.rb +1 -1
  101. data/lib/httpx/plugins/rate_limiter.rb +3 -1
  102. data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
  103. data/lib/httpx/plugins/response_cache/store.rb +34 -17
  104. data/lib/httpx/plugins/response_cache.rb +6 -6
  105. data/lib/httpx/plugins/retries.rb +61 -12
  106. data/lib/httpx/plugins/ssrf_filter.rb +142 -0
  107. data/lib/httpx/plugins/stream.rb +27 -32
  108. data/lib/httpx/plugins/upgrade/h2.rb +4 -4
  109. data/lib/httpx/plugins/upgrade.rb +8 -10
  110. data/lib/httpx/plugins/webdav.rb +10 -8
  111. data/lib/httpx/pool.rb +85 -23
  112. data/lib/httpx/punycode.rb +9 -291
  113. data/lib/httpx/request/body.rb +158 -0
  114. data/lib/httpx/request.rb +86 -121
  115. data/lib/httpx/resolver/https.rb +54 -17
  116. data/lib/httpx/resolver/multi.rb +8 -12
  117. data/lib/httpx/resolver/native.rb +163 -70
  118. data/lib/httpx/resolver/resolver.rb +28 -13
  119. data/lib/httpx/resolver/system.rb +15 -10
  120. data/lib/httpx/resolver.rb +38 -16
  121. data/lib/httpx/response/body.rb +242 -0
  122. data/lib/httpx/response/buffer.rb +96 -0
  123. data/lib/httpx/response.rb +113 -211
  124. data/lib/httpx/selector.rb +2 -4
  125. data/lib/httpx/session.rb +91 -64
  126. data/lib/httpx/session_extensions.rb +4 -1
  127. data/lib/httpx/timers.rb +28 -8
  128. data/lib/httpx/transcoder/body.rb +0 -2
  129. data/lib/httpx/transcoder/chunker.rb +0 -1
  130. data/lib/httpx/transcoder/deflate.rb +37 -0
  131. data/lib/httpx/transcoder/form.rb +52 -33
  132. data/lib/httpx/transcoder/gzip.rb +74 -0
  133. data/lib/httpx/transcoder/json.rb +2 -5
  134. data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
  135. data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +3 -3
  136. data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
  137. data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
  138. data/lib/httpx/transcoder/multipart.rb +17 -0
  139. data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
  140. data/lib/httpx/transcoder/utils/deflater.rb +72 -0
  141. data/lib/httpx/transcoder/utils/inflater.rb +19 -0
  142. data/lib/httpx/transcoder/xml.rb +0 -5
  143. data/lib/httpx/transcoder.rb +4 -6
  144. data/lib/httpx/utils.rb +36 -16
  145. data/lib/httpx/version.rb +1 -1
  146. data/lib/httpx.rb +12 -14
  147. data/sig/altsvc.rbs +33 -0
  148. data/sig/buffer.rbs +1 -0
  149. data/sig/callbacks.rbs +3 -3
  150. data/sig/chainable.rbs +10 -9
  151. data/sig/connection/http1.rbs +5 -4
  152. data/sig/connection/http2.rbs +1 -1
  153. data/sig/connection.rbs +46 -24
  154. data/sig/errors.rbs +9 -3
  155. data/sig/httpx.rbs +5 -4
  156. data/sig/io/ssl.rbs +26 -0
  157. data/sig/io/tcp.rbs +60 -0
  158. data/sig/io/udp.rbs +20 -0
  159. data/sig/io/unix.rbs +10 -0
  160. data/sig/options.rbs +28 -12
  161. data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
  162. data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
  163. data/sig/plugins/auth.rbs +13 -0
  164. data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
  165. data/sig/plugins/brotli.rbs +22 -0
  166. data/sig/plugins/callbacks.rbs +38 -0
  167. data/sig/plugins/circuit_breaker.rbs +13 -3
  168. data/sig/plugins/compression.rbs +6 -4
  169. data/sig/plugins/cookies/jar.rbs +2 -2
  170. data/sig/plugins/cookies.rbs +2 -0
  171. data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
  172. data/sig/plugins/follow_redirects.rbs +11 -2
  173. data/sig/plugins/grpc/call.rbs +19 -0
  174. data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
  175. data/sig/plugins/grpc/message.rbs +17 -0
  176. data/sig/plugins/grpc.rbs +2 -32
  177. data/sig/plugins/h2c.rbs +1 -1
  178. data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
  179. data/sig/plugins/oauth.rbs +54 -0
  180. data/sig/plugins/proxy/socks4.rbs +4 -4
  181. data/sig/plugins/proxy/socks5.rbs +2 -2
  182. data/sig/plugins/proxy/ssh.rbs +1 -1
  183. data/sig/plugins/proxy.rbs +10 -4
  184. data/sig/plugins/response_cache.rbs +12 -3
  185. data/sig/plugins/retries.rbs +28 -8
  186. data/sig/plugins/stream.rbs +24 -17
  187. data/sig/plugins/upgrade.rbs +5 -3
  188. data/sig/pool.rbs +5 -4
  189. data/sig/request/body.rbs +40 -0
  190. data/sig/request.rbs +12 -28
  191. data/sig/resolver/https.rbs +7 -2
  192. data/sig/resolver/native.rbs +10 -4
  193. data/sig/resolver/resolver.rbs +6 -4
  194. data/sig/resolver/system.rbs +2 -0
  195. data/sig/resolver.rbs +9 -5
  196. data/sig/response/body.rbs +53 -0
  197. data/sig/response/buffer.rbs +24 -0
  198. data/sig/response.rbs +17 -38
  199. data/sig/session.rbs +24 -18
  200. data/sig/timers.rbs +17 -7
  201. data/sig/transcoder/body.rbs +4 -3
  202. data/sig/transcoder/deflate.rbs +11 -0
  203. data/sig/transcoder/form.rbs +5 -3
  204. data/sig/transcoder/gzip.rbs +24 -0
  205. data/sig/transcoder/json.rbs +4 -2
  206. data/sig/{plugins → transcoder}/multipart.rbs +3 -12
  207. data/sig/transcoder/utils/body_reader.rbs +15 -0
  208. data/sig/transcoder/utils/deflater.rbs +29 -0
  209. data/sig/transcoder/utils/inflater.rbs +12 -0
  210. data/sig/transcoder/xml.rbs +1 -1
  211. data/sig/transcoder.rbs +22 -7
  212. data/sig/utils.rbs +2 -0
  213. metadata +127 -40
  214. data/lib/httpx/plugins/authentication.rb +0 -20
  215. data/lib/httpx/plugins/basic_authentication.rb +0 -30
  216. data/lib/httpx/plugins/compression/brotli.rb +0 -54
  217. data/lib/httpx/plugins/compression/deflate.rb +0 -49
  218. data/lib/httpx/plugins/compression/gzip.rb +0 -88
  219. data/lib/httpx/plugins/compression.rb +0 -164
  220. data/lib/httpx/plugins/multipart/decoder.rb +0 -187
  221. data/lib/httpx/plugins/multipart.rb +0 -84
  222. data/lib/httpx/registry.rb +0 -85
  223. data/sig/plugins/authentication.rbs +0 -11
  224. data/sig/plugins/compression/brotli.rbs +0 -21
  225. data/sig/plugins/compression/deflate.rbs +0 -17
  226. data/sig/plugins/compression/gzip.rbs +0 -29
  227. data/sig/registry.rbs +0 -13
  228. /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
  229. /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -27,6 +27,11 @@ module HTTPX::Plugins
27
27
  def set_sentry_trace_header(request, sentry_span)
28
28
  return unless sentry_span
29
29
 
30
+ config = ::Sentry.configuration
31
+ url = request.uri.to_s
32
+
33
+ return unless config.propagate_traces && config.trace_propagation_targets.any? { |target| url.match?(target) }
34
+
30
35
  trace = ::Sentry.get_current_client.generate_sentry_trace(sentry_span)
31
36
  request.headers[::Sentry::SENTRY_TRACE_HEADER_NAME] = trace if trace
32
37
  end
@@ -43,8 +48,8 @@ module HTTPX::Plugins
43
48
 
44
49
  request_info = extract_request_info(req)
45
50
 
46
- data = if response.is_a?(HTTPX::ErrorResponse)
47
- { error: res.message, **request_info }
51
+ data = if res.is_a?(HTTPX::ErrorResponse)
52
+ { error: res.error.message, **request_info }
48
53
  else
49
54
  { status: res.status, **request_info }
50
55
  end
@@ -63,7 +68,11 @@ module HTTPX::Plugins
63
68
 
64
69
  request_info = extract_request_info(req)
65
70
  sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
66
- sentry_span.set_data(:status, res.status)
71
+ if res.is_a?(HTTPX::ErrorResponse)
72
+ sentry_span.set_data(:error, res.error.message)
73
+ else
74
+ sentry_span.set_data(:status, res.status)
75
+ end
67
76
  sentry_span.set_timestamp(::Sentry.utc_now.to_f)
68
77
  end
69
78
 
@@ -71,7 +80,7 @@ module HTTPX::Plugins
71
80
  uri = req.uri
72
81
 
73
82
  result = {
74
- method: req.verb.to_s.upcase,
83
+ method: req.verb,
75
84
  }
76
85
 
77
86
  if ::Sentry.configuration.send_default_pii
@@ -85,17 +94,27 @@ module HTTPX::Plugins
85
94
  end
86
95
  end
87
96
 
97
+ module RequestMethods
98
+ def __sentry_enable_trace!
99
+ return if @__sentry_enable_trace
100
+
101
+ Tracer.call(self)
102
+ @__sentry_enable_trace = true
103
+ end
104
+ end
105
+
88
106
  module ConnectionMethods
89
107
  def send(request)
90
- Tracer.call(request)
108
+ request.__sentry_enable_trace!
109
+
91
110
  super
92
111
  end
93
112
  end
94
113
  end
95
114
  end
96
115
 
97
- Sentry.register_patch do
98
- sentry_session = ::HTTPX.plugin(HTTPX::Plugins::Sentry)
116
+ Sentry.register_patch(:httpx) do
117
+ sentry_session = HTTPX.plugin(HTTPX::Plugins::Sentry)
99
118
 
100
119
  HTTPX.send(:remove_const, :Session)
101
120
  HTTPX.send(:const_set, :Session, sentry_session.class)
@@ -2,13 +2,8 @@
2
2
 
3
3
  module WebMock
4
4
  module HttpLibAdapters
5
- if RUBY_VERSION < "2.5"
6
- require "webrick/httpstatus"
7
- HTTP_REASONS = WEBrick::HTTPStatus::StatusMessage
8
- else
9
- require "net/http/status"
10
- HTTP_REASONS = Net::HTTP::STATUS_CODES
11
- end
5
+ require "net/http/status"
6
+ HTTP_REASONS = Net::HTTP::STATUS_CODES
12
7
 
13
8
  #
14
9
  # HTTPX plugin for webmock.
@@ -23,7 +18,7 @@ module WebMock
23
18
  uri.path = uri.normalized_path.gsub("[^:]//", "/")
24
19
 
25
20
  WebMock::RequestSignature.new(
26
- request.verb,
21
+ request.verb.downcase.to_sym,
27
22
  uri.to_s,
28
23
  body: request.body.each.to_a.join,
29
24
  headers: request.headers.to_h
@@ -43,12 +38,12 @@ module WebMock
43
38
 
44
39
  return build_error_response(request, webmock_response.exception) if webmock_response.exception
45
40
 
46
- response = request.options.response_class.new(request,
47
- webmock_response.status[0],
48
- "2.0",
49
- webmock_response.headers)
50
- response << webmock_response.body.dup
51
- response
41
+ request.options.response_class.new(request,
42
+ webmock_response.status[0],
43
+ "2.0",
44
+ webmock_response.headers).tap do |res|
45
+ res.mocked = true
46
+ end
52
47
  end
53
48
 
54
49
  def build_error_response(request, exception)
@@ -57,16 +52,36 @@ module WebMock
57
52
  end
58
53
 
59
54
  module InstanceMethods
60
- def build_connection(*)
55
+ def init_connection(*)
61
56
  connection = super
62
57
  connection.once(:unmock_connection) do
58
+ unless connection.addresses
59
+ connection.__send__(:callbacks)[:connect_error].clear
60
+ pool.__send__(:unregister_connection, connection)
61
+ end
63
62
  pool.__send__(:resolve_connection, connection)
64
- pool.__send__(:unregister_connection, connection) unless connection.addresses
65
63
  end
66
64
  connection
67
65
  end
68
66
  end
69
67
 
68
+ module ResponseMethods
69
+ attr_accessor :mocked
70
+
71
+ def initialize(*)
72
+ super
73
+ @mocked = false
74
+ end
75
+ end
76
+
77
+ module ResponseBodyMethods
78
+ def decode_chunk(chunk)
79
+ return chunk if @response.mocked
80
+
81
+ super
82
+ end
83
+ end
84
+
70
85
  module ConnectionMethods
71
86
  def initialize(*)
72
87
  super
@@ -95,6 +110,7 @@ module WebMock
95
110
  log { "mocking #{request.uri} with #{mock_response.inspect}" }
96
111
  request.response = response
97
112
  request.emit(:response, response)
113
+ response << mock_response.body.dup unless response.is_a?(HTTPX::ErrorResponse)
98
114
  elsif WebMock.net_connect_allowed?(request_signature.uri)
99
115
  if WebMock::CallbackRegistry.any_callbacks?
100
116
  request.on(:response) do |resp|
@@ -122,7 +138,7 @@ module WebMock
122
138
 
123
139
  class << self
124
140
  def enable!
125
- @original_session = HTTPX::Session
141
+ @original_session ||= HTTPX::Session
126
142
 
127
143
  webmock_session = HTTPX.plugin(Plugin)
128
144
 
data/lib/httpx/altsvc.rb CHANGED
@@ -4,7 +4,59 @@ require "strscan"
4
4
 
5
5
  module HTTPX
6
6
  module AltSvc
7
- @altsvc_mutex = Mutex.new
7
+ # makes connections able to accept requests destined to primary service.
8
+ module ConnectionMixin
9
+ using URIExtensions
10
+
11
+ def send(request)
12
+ request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
13
+
14
+ super
15
+ end
16
+
17
+ def match?(uri, options)
18
+ return false if !used? && (@state == :closing || @state == :closed)
19
+
20
+ match_altsvcs?(uri) && match_altsvc_options?(uri, options)
21
+ end
22
+
23
+ private
24
+
25
+ # checks if this is connection is an alternative service of
26
+ # +uri+
27
+ def match_altsvcs?(uri)
28
+ @origins.any? { |origin| altsvc_match?(uri, origin) } ||
29
+ AltSvc.cached_altsvc(@origin).any? do |altsvc|
30
+ origin = altsvc["origin"]
31
+ altsvc_match?(origin, uri.origin)
32
+ end
33
+ end
34
+
35
+ def match_altsvc_options?(uri, options)
36
+ return @options == options unless @options.ssl.all? do |k, v|
37
+ v == (k == :hostname ? uri.host : options.ssl[k])
38
+ end
39
+
40
+ @options.options_equals?(options, Options::REQUEST_BODY_IVARS + %i[@ssl])
41
+ end
42
+
43
+ def altsvc_match?(uri, other_uri)
44
+ other_uri = URI(other_uri)
45
+
46
+ uri.origin == other_uri.origin || begin
47
+ case uri.scheme
48
+ when "h2"
49
+ (other_uri.scheme == "https" || other_uri.scheme == "h2") &&
50
+ uri.host == other_uri.host &&
51
+ uri.port == other_uri.port
52
+ else
53
+ false
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ @altsvc_mutex = Thread::Mutex.new
8
60
  @altsvcs = Hash.new { |h, k| h[k] = [] }
9
61
 
10
62
  module_function
@@ -46,7 +98,7 @@ module HTTPX
46
98
 
47
99
  altsvc = response.headers["alt-svc"]
48
100
 
49
- # https://tools.ietf.org/html/rfc7838#section-3
101
+ # https://datatracker.ietf.org/doc/html/rfc7838#section-3
50
102
  # A field value containing the special value "clear" indicates that the
51
103
  # origin requests all alternatives for that origin to be invalidated
52
104
  # (including those specified in the same response, in case of an
@@ -98,29 +150,14 @@ module HTTPX
98
150
  end
99
151
  end
100
152
 
101
- # :nocov:
102
- if RUBY_VERSION < "2.2"
103
- def parse_altsvc_origin(alt_proto, alt_origin)
104
- alt_scheme = parse_altsvc_scheme(alt_proto) or return
105
-
106
- alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
107
- if alt_origin.start_with?(":")
108
- alt_origin = "#{alt_scheme}://dummy#{alt_origin}"
109
- uri = URI.parse(alt_origin)
110
- uri.host = nil
111
- uri
112
- else
113
- URI.parse("#{alt_scheme}://#{alt_origin}")
114
- end
115
- end
116
- else
117
- def parse_altsvc_origin(alt_proto, alt_origin)
118
- alt_scheme = parse_altsvc_scheme(alt_proto) or return
119
- alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
153
+ def parse_altsvc_origin(alt_proto, alt_origin)
154
+ alt_scheme = parse_altsvc_scheme(alt_proto)
120
155
 
121
- URI.parse("#{alt_scheme}://#{alt_origin}")
122
- end
156
+ return unless alt_scheme
157
+
158
+ alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
159
+
160
+ URI.parse("#{alt_scheme}://#{alt_origin}")
123
161
  end
124
- # :nocov:
125
162
  end
126
163
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ if RUBY_VERSION < "3.3.0"
4
+ require "base64"
5
+ elsif !defined?(Base64)
6
+ module HTTPX
7
+ # require "base64" will not be a default gem after ruby 3.4.0
8
+ module Base64
9
+ module_function
10
+
11
+ def decode64(str)
12
+ str.unpack1("m")
13
+ end
14
+
15
+ def strict_encode64(bin)
16
+ [bin].pack("m0")
17
+ end
18
+
19
+ def urlsafe_encode64(bin, padding: true)
20
+ str = strict_encode64(bin)
21
+ str.chomp!("==") or str.chomp!("=") unless padding
22
+ str.tr!("+/", "-_")
23
+ str
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/httpx/buffer.rb CHANGED
@@ -3,6 +3,14 @@
3
3
  require "forwardable"
4
4
 
5
5
  module HTTPX
6
+ # Internal class to abstract a string buffer, by wrapping a string and providing the
7
+ # minimum possible API and functionality required.
8
+ #
9
+ # buffer = Buffer.new(640)
10
+ # buffer.full? #=> false
11
+ # buffer << "aa"
12
+ # buffer.capacity #=> 638
13
+ #
6
14
  class Buffer
7
15
  extend Forwardable
8
16
 
@@ -31,6 +39,10 @@ module HTTPX
31
39
  @buffer.bytesize >= @limit
32
40
  end
33
41
 
42
+ def capacity
43
+ @limit - @buffer.bytesize
44
+ end
45
+
34
46
  def shift!(fin)
35
47
  @buffer = @buffer.byteslice(fin..-1) || "".b
36
48
  end
@@ -4,6 +4,7 @@ module HTTPX
4
4
  module Callbacks
5
5
  def on(type, &action)
6
6
  callbacks(type) << action
7
+ self
7
8
  end
8
9
 
9
10
  def once(type, &block)
@@ -11,6 +12,7 @@ module HTTPX
11
12
  block.call(*args, &callback)
12
13
  :delete
13
14
  end
15
+ self
14
16
  end
15
17
 
16
18
  def only(type, &block)
@@ -22,12 +24,12 @@ module HTTPX
22
24
  callbacks(type).delete_if { |pr| :delete == pr.call(*args) } # rubocop:disable Style/YodaCondition
23
25
  end
24
26
 
25
- protected
26
-
27
27
  def callbacks_for?(type)
28
- @callbacks.key?(type) && !@callbacks[type].empty?
28
+ @callbacks.key?(type) && @callbacks[type].any?
29
29
  end
30
30
 
31
+ protected
32
+
31
33
  def callbacks(type = nil)
32
34
  return @callbacks unless type
33
35
 
@@ -1,89 +1,104 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
+ # Session mixin, implements most of the APIs that the users call.
5
+ # delegates to a default session when extended.
4
6
  module Chainable
5
- %i[head get post put delete trace options connect patch].each do |meth|
7
+ %w[head get post put delete trace options connect patch].each do |meth|
6
8
  class_eval(<<-MOD, __FILE__, __LINE__ + 1)
7
- def #{meth}(*uri, **options) # def get(*uri, **options)
8
- request(:#{meth}, uri, **options) # request(:get, uri, **options)
9
- end # end
9
+ def #{meth}(*uri, **options) # def get(*uri, **options)
10
+ request("#{meth.upcase}", uri, **options) # request("GET", uri, **options)
11
+ end # end
10
12
  MOD
11
13
  end
12
14
 
15
+ # delegates to the default session (see HTTPX::Session#request).
13
16
  def request(*args, **options)
14
17
  branch(default_options).request(*args, **options)
15
18
  end
16
19
 
17
- # :nocov:
18
- def timeout(**args)
19
- warn ":#{__method__} is deprecated, use :with_timeout instead"
20
- with(timeout: args)
21
- end
22
-
23
- def headers(headers)
24
- warn ":#{__method__} is deprecated, use :with_headers instead"
25
- with(headers: headers)
26
- end
27
- # :nocov:
28
-
29
20
  def accept(type)
30
21
  with(headers: { "accept" => String(type) })
31
22
  end
32
23
 
24
+ # delegates to the default session (see HTTPX::Session#wrap).
33
25
  def wrap(&blk)
34
26
  branch(default_options).wrap(&blk)
35
27
  end
36
28
 
29
+ # returns a new instance loaded with the +pl+ plugin and +options+.
37
30
  def plugin(pl, options = nil, &blk)
38
- klass = is_a?(Session) ? self.class : Session
31
+ klass = is_a?(S) ? self.class : Session
39
32
  klass = Class.new(klass)
40
33
  klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
41
34
  klass.plugin(pl, options, &blk).new
42
35
  end
43
36
 
44
- # deprecated
45
- # :nocov:
46
- def plugins(pls)
47
- warn ":#{__method__} is deprecated, use :plugin instead"
48
- klass = is_a?(Session) ? self.class : Session
49
- klass = Class.new(klass)
50
- klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
51
- klass.plugins(pls).new
52
- end
53
- # :nocov:
54
-
37
+ # returns a new instance loaded with +options+.
55
38
  def with(options, &blk)
56
39
  branch(default_options.merge(options), &blk)
57
40
  end
58
41
 
59
42
  private
60
43
 
44
+ # returns default instance of HTTPX::Options.
61
45
  def default_options
62
46
  @options || Session.default_options
63
47
  end
64
48
 
49
+ # returns a default instance of HTTPX::Session.
65
50
  def branch(options, &blk)
66
- return self.class.new(options, &blk) if is_a?(Session)
51
+ return self.class.new(options, &blk) if is_a?(S)
67
52
 
68
53
  Session.new(options, &blk)
69
54
  end
70
55
 
71
- def method_missing(meth, *args, **options)
72
- return super unless meth =~ /\Awith_(.+)/
56
+ def method_missing(meth, *args, **options, &blk)
57
+ case meth
58
+ when /\Awith_(.+)/
73
59
 
74
- option = Regexp.last_match(1)
60
+ option = Regexp.last_match(1)
75
61
 
76
- return super unless option
62
+ return super unless option
77
63
 
78
- with(option.to_sym => (args.first || options))
79
- end
64
+ with(option.to_sym => args.first || options)
65
+ when /\Aon_(.+)/
66
+ callback = Regexp.last_match(1)
80
67
 
81
- def respond_to_missing?(meth, *)
82
- return super unless meth =~ /\Awith_(.+)/
68
+ return super unless %w[
69
+ connection_opened connection_closed
70
+ request_error
71
+ request_started request_body_chunk request_completed
72
+ response_started response_body_chunk response_completed
73
+ ].include?(callback)
83
74
 
84
- option = Regexp.last_match(1)
75
+ warn "DEPRECATION WARNING: calling `.#{meth}` on plain HTTPX sessions is deprecated. " \
76
+ "Use HTTPX.plugin(:callbacks).#{meth} instead."
85
77
 
86
- default_options.respond_to?(option) || super
78
+ plugin(:callbacks).__send__(meth, *args, **options, &blk)
79
+ else
80
+ super
81
+ end
82
+ end
83
+
84
+ def respond_to_missing?(meth, *)
85
+ case meth
86
+ when /\Awith_(.+)/
87
+ option = Regexp.last_match(1)
88
+
89
+ default_options.respond_to?(option) || super
90
+ when /\Aon_(.+)/
91
+ callback = Regexp.last_match(1)
92
+
93
+ %w[
94
+ connection_opened connection_closed
95
+ request_error
96
+ request_started request_body_chunk request_completed
97
+ response_started response_body_chunk response_completed
98
+ ].include?(callback) || super
99
+ else
100
+ super
101
+ end
87
102
  end
88
103
  end
89
104
  end