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
@@ -1,51 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- if defined?(::DDTrace) && ::DDTrace::VERSION::STRING >= "1.0.0"
4
- require "datadog/tracing/contrib/integration"
5
- require "datadog/tracing/contrib/configuration/settings"
6
- require "datadog/tracing/contrib/patcher"
3
+ require "datadog/tracing/contrib/integration"
4
+ require "datadog/tracing/contrib/configuration/settings"
5
+ require "datadog/tracing/contrib/patcher"
7
6
 
8
- TRACING_MODULE = Datadog::Tracing
9
- else
10
-
11
- require "ddtrace/contrib/integration"
12
- require "ddtrace/contrib/configuration/settings"
13
- require "ddtrace/contrib/patcher"
14
-
15
- TRACING_MODULE = Datadog
16
- end
17
-
18
- module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
7
+ module Datadog::Tracing
19
8
  module Contrib
20
9
  module HTTPX
21
- if defined?(::DDTrace) && ::DDTrace::VERSION::STRING >= "1.0.0"
22
- METADATA_MODULE = TRACING_MODULE::Metadata
23
-
24
- TYPE_OUTBOUND = TRACING_MODULE::Metadata::Ext::HTTP::TYPE_OUTBOUND
25
-
26
- TAG_PEER_SERVICE = TRACING_MODULE::Metadata::Ext::TAG_PEER_SERVICE
10
+ METADATA_MODULE = Datadog::Tracing::Metadata
27
11
 
28
- TAG_URL = TRACING_MODULE::Metadata::Ext::HTTP::TAG_URL
29
- TAG_METHOD = TRACING_MODULE::Metadata::Ext::HTTP::TAG_METHOD
30
- TAG_TARGET_HOST = TRACING_MODULE::Metadata::Ext::NET::TAG_TARGET_HOST
31
- TAG_TARGET_PORT = TRACING_MODULE::Metadata::Ext::NET::TAG_TARGET_PORT
12
+ TYPE_OUTBOUND = Datadog::Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
32
13
 
33
- TAG_STATUS_CODE = TRACING_MODULE::Metadata::Ext::HTTP::TAG_STATUS_CODE
14
+ TAG_PEER_SERVICE = Datadog::Tracing::Metadata::Ext::TAG_PEER_SERVICE
34
15
 
35
- else
16
+ TAG_URL = Datadog::Tracing::Metadata::Ext::HTTP::TAG_URL
17
+ TAG_METHOD = Datadog::Tracing::Metadata::Ext::HTTP::TAG_METHOD
18
+ TAG_TARGET_HOST = Datadog::Tracing::Metadata::Ext::NET::TAG_TARGET_HOST
19
+ TAG_TARGET_PORT = Datadog::Tracing::Metadata::Ext::NET::TAG_TARGET_PORT
36
20
 
37
- METADATA_MODULE = Datadog
38
-
39
- TYPE_OUTBOUND = TRACING_MODULE::Ext::HTTP::TYPE_OUTBOUND
40
- TAG_PEER_SERVICE = TRACING_MODULE::Ext::Integration::TAG_PEER_SERVICE
41
- TAG_URL = TRACING_MODULE::Ext::HTTP::URL
42
- TAG_METHOD = TRACING_MODULE::Ext::HTTP::METHOD
43
- TAG_TARGET_HOST = TRACING_MODULE::Ext::NET::TARGET_HOST
44
- TAG_TARGET_PORT = TRACING_MODULE::Ext::NET::TARGET_PORT
45
- TAG_STATUS_CODE = Datadog::Ext::HTTP::STATUS_CODE
46
- PROPAGATOR = TRACING_MODULE::HTTPPropagator
47
-
48
- end
21
+ TAG_STATUS_CODE = Datadog::Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE
49
22
 
50
23
  # HTTPX Datadog Plugin
51
24
  #
@@ -64,14 +37,18 @@ module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
64
37
  end
65
38
 
66
39
  def call
67
- return unless tracing_enabled?
40
+ return unless Datadog::Tracing.enabled?
68
41
 
69
42
  @request.on(:response, &method(:finish))
70
43
 
71
- verb = @request.verb.to_s.upcase
44
+ verb = @request.verb
72
45
  uri = @request.uri
73
46
 
74
- @span = build_span
47
+ @span = Datadog::Tracing.trace(
48
+ SPAN_REQUEST,
49
+ service: service_name(@request.uri.host, configuration, Datadog.configuration_for(self)),
50
+ span_type: TYPE_OUTBOUND
51
+ )
75
52
 
76
53
  @span.resource = verb
77
54
 
@@ -86,7 +63,8 @@ module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
86
63
  # Tag as an external peer service
87
64
  @span.set_tag(TAG_PEER_SERVICE, @span.service)
88
65
 
89
- propagate_headers if @configuration[:distributed_tracing]
66
+ Datadog::Tracing::Propagation::HTTP.inject!(Datadog::Tracing.active_trace,
67
+ @request.headers) if @configuration[:distributed_tracing]
90
68
 
91
69
  # Set analytics sample rate
92
70
  if Contrib::Analytics.enabled?(@configuration[:analytics_enabled])
@@ -113,54 +91,24 @@ module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
113
91
 
114
92
  private
115
93
 
116
- if defined?(::DDTrace) && ::DDTrace::VERSION::STRING >= "1.0.0"
117
-
118
- def build_span
119
- TRACING_MODULE.trace(
120
- SPAN_REQUEST,
121
- service: service_name(@request.uri.host, configuration, Datadog.configuration_for(self)),
122
- span_type: TYPE_OUTBOUND
123
- )
124
- end
125
-
126
- def propagate_headers
127
- TRACING_MODULE::Propagation::HTTP.inject!(TRACING_MODULE.active_trace, @request.headers)
128
- end
129
-
130
- def configuration
131
- @configuration ||= Datadog.configuration.tracing[:httpx, @request.uri.host]
132
- end
133
-
134
- def tracing_enabled?
135
- TRACING_MODULE.enabled?
136
- end
137
- else
138
- def build_span
139
- service_name = configuration[:split_by_domain] ? @request.uri.host : configuration[:service_name]
140
- configuration[:tracer].trace(
141
- SPAN_REQUEST,
142
- service: service_name,
143
- span_type: TYPE_OUTBOUND
144
- )
145
- end
146
-
147
- def propagate_headers
148
- Datadog::HTTPPropagator.inject!(@span.context, @request.headers)
149
- end
94
+ def configuration
95
+ @configuration ||= Datadog.configuration.tracing[:httpx, @request.uri.host]
96
+ end
97
+ end
150
98
 
151
- def configuration
152
- @configuration ||= Datadog.configuration[:httpx, @request.uri.host]
153
- end
99
+ module RequestMethods
100
+ def __datadog_enable_trace!
101
+ return if @__datadog_enable_trace
154
102
 
155
- def tracing_enabled?
156
- configuration[:tracer].enabled
157
- end
103
+ RequestTracer.new(self).call
104
+ @__datadog_enable_trace = true
158
105
  end
159
106
  end
160
107
 
161
108
  module ConnectionMethods
162
109
  def send(request)
163
- RequestTracer.new(request).call
110
+ request.__datadog_enable_trace!
111
+
164
112
  super
165
113
  end
166
114
  end
@@ -169,7 +117,7 @@ module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
169
117
  module Configuration
170
118
  # Default settings for httpx
171
119
  #
172
- class Settings < TRACING_MODULE::Contrib::Configuration::Settings
120
+ class Settings < Datadog::Tracing::Contrib::Configuration::Settings
173
121
  DEFAULT_ERROR_HANDLER = lambda do |response|
174
122
  Datadog::Ext::HTTP::ERROR_RANGE.cover?(response.status)
175
123
  end
@@ -178,29 +126,82 @@ module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
178
126
  option :distributed_tracing, default: true
179
127
  option :split_by_domain, default: false
180
128
 
181
- option :enabled do |o|
182
- o.default { env_to_bool("DD_TRACE_HTTPX_ENABLED", true) }
183
- o.lazy
184
- end
129
+ if Gem::Version.new(DDTrace::VERSION::STRING) >= Gem::Version.new("1.13.0")
130
+ option :enabled do |o|
131
+ o.type :bool
132
+ o.env "DD_TRACE_HTTPX_ENABLED"
133
+ o.default true
134
+ end
135
+
136
+ option :analytics_enabled do |o|
137
+ o.type :bool
138
+ o.env "DD_TRACE_HTTPX_ANALYTICS_ENABLED"
139
+ o.default false
140
+ end
141
+
142
+ option :analytics_sample_rate do |o|
143
+ o.type :float
144
+ o.env "DD_TRACE_HTTPX_ANALYTICS_SAMPLE_RATE"
145
+ o.default 1.0
146
+ end
147
+ else
148
+ option :enabled do |o|
149
+ o.default { env_to_bool("DD_TRACE_HTTPX_ENABLED", true) }
150
+ o.lazy
151
+ end
152
+
153
+ option :analytics_enabled do |o|
154
+ o.default { env_to_bool(%w[DD_TRACE_HTTPX_ANALYTICS_ENABLED DD_HTTPX_ANALYTICS_ENABLED], false) }
155
+ o.lazy
156
+ end
185
157
 
186
- option :analytics_enabled do |o|
187
- o.default { env_to_bool(%w[DD_TRACE_HTTPX_ANALYTICS_ENABLED DD_HTTPX_ANALYTICS_ENABLED], false) }
188
- o.lazy
158
+ option :analytics_sample_rate do |o|
159
+ o.default { env_to_float(%w[DD_TRACE_HTTPX_ANALYTICS_SAMPLE_RATE DD_HTTPX_ANALYTICS_SAMPLE_RATE], 1.0) }
160
+ o.lazy
161
+ end
189
162
  end
190
163
 
191
- option :analytics_sample_rate do |o|
192
- o.default { env_to_float(%w[DD_TRACE_HTTPX_ANALYTICS_SAMPLE_RATE DD_HTTPX_ANALYTICS_SAMPLE_RATE], 1.0) }
193
- o.lazy
164
+ if defined?(Datadog::Tracing::Contrib::SpanAttributeSchema)
165
+ option :service_name do |o|
166
+ o.default do
167
+ Datadog::Tracing::Contrib::SpanAttributeSchema.fetch_service_name(
168
+ "DD_TRACE_HTTPX_SERVICE_NAME",
169
+ "httpx"
170
+ )
171
+ end
172
+ o.lazy
173
+ end
174
+ else
175
+ option :service_name do |o|
176
+ o.default do
177
+ ENV.fetch("DD_TRACE_HTTPX_SERVICE_NAME", "httpx")
178
+ end
179
+ o.lazy
180
+ end
194
181
  end
195
182
 
196
- option :error_handler, default: DEFAULT_ERROR_HANDLER
183
+ option :distributed_tracing, default: true
184
+
185
+ if Gem::Version.new(DDTrace::VERSION::STRING) >= Gem::Version.new("1.15.0")
186
+ option :error_handler do |o|
187
+ o.type :proc
188
+ o.default_proc(&DEFAULT_ERROR_HANDLER)
189
+ end
190
+ elsif Gem::Version.new(DDTrace::VERSION::STRING) >= Gem::Version.new("1.13.0")
191
+ option :error_handler do |o|
192
+ o.type :proc
193
+ o.experimental_default_proc(&DEFAULT_ERROR_HANDLER)
194
+ end
195
+ else
196
+ option :error_handler, default: DEFAULT_ERROR_HANDLER
197
+ end
197
198
  end
198
199
  end
199
200
 
200
201
  # Patcher enables patching of 'httpx' with datadog components.
201
202
  #
202
203
  module Patcher
203
- include TRACING_MODULE::Contrib::Patcher
204
+ include Datadog::Tracing::Contrib::Patcher
204
205
 
205
206
  module_function
206
207
 
@@ -223,7 +224,6 @@ module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
223
224
  class Integration
224
225
  include Contrib::Integration
225
226
 
226
- # MINIMUM_VERSION = Gem::Version.new('0.11.0')
227
227
  MINIMUM_VERSION = Gem::Version.new("0.10.2")
228
228
 
229
229
  register_as :httpx
@@ -240,14 +240,8 @@ module TRACING_MODULE # rubocop:disable Naming/ClassAndModuleCamelCase
240
240
  super && version >= MINIMUM_VERSION
241
241
  end
242
242
 
243
- if defined?(::DDTrace) && ::DDTrace::VERSION::STRING >= "1.0.0"
244
- def new_configuration
245
- Configuration::Settings.new
246
- end
247
- else
248
- def default_configuration
249
- Configuration::Settings.new
250
- end
243
+ def new_configuration
244
+ Configuration::Settings.new
251
245
  end
252
246
 
253
247
  def patcher
@@ -7,36 +7,54 @@ require "faraday"
7
7
  module Faraday
8
8
  class Adapter
9
9
  class HTTPX < Faraday::Adapter
10
- # :nocov:
11
- SSL_ERROR = if defined?(Faraday::SSLError)
12
- Faraday::SSLError
13
- else
14
- Faraday::Error::SSLError
15
- end
10
+ module RequestMixin
11
+ def build_connection(env)
12
+ return @connection if defined?(@connection)
16
13
 
17
- CONNECTION_FAILED_ERROR = if defined?(Faraday::ConnectionFailed)
18
- Faraday::ConnectionFailed
19
- else
20
- Faraday::Error::ConnectionFailed
21
- end
22
- # :nocov:
14
+ @connection = ::HTTPX.plugin(:persistent).plugin(ReasonPlugin)
15
+ @connection = @connection.with(@connection_options) unless @connection_options.empty?
16
+ connection_opts = options_from_env(env)
23
17
 
24
- unless Faraday::RequestOptions.method_defined?(:stream_response?)
25
- module RequestOptionsExtensions
26
- refine Faraday::RequestOptions do
27
- def stream_response?
28
- false
29
- end
18
+ if (bind = env.request.bind)
19
+ @bind = TCPSocket.new(bind[:host], bind[:port])
20
+ connection_opts[:io] = @bind
21
+ end
22
+ @connection = @connection.with(connection_opts)
23
+
24
+ if (proxy = env.request.proxy)
25
+ proxy_options = { uri: proxy.uri }
26
+ proxy_options[:username] = proxy.user if proxy.user
27
+ proxy_options[:password] = proxy.password if proxy.password
28
+
29
+ @connection = @connection.plugin(:proxy).with(proxy: proxy_options)
30
30
  end
31
+ @connection = @connection.plugin(OnDataPlugin) if env.request.stream_response?
32
+
33
+ @connection
31
34
  end
32
- using RequestOptionsExtensions
33
- end
34
35
 
35
- module RequestMixin
36
- using ::HTTPX::HashExtensions
36
+ def close
37
+ @connection.close if @connection
38
+ @bind.close if @bind
39
+ end
37
40
 
38
41
  private
39
42
 
43
+ def connect(env, &blk)
44
+ connection(env, &blk)
45
+ rescue ::HTTPX::TLSError => e
46
+ raise Faraday::SSLError, e
47
+ rescue Errno::ECONNABORTED,
48
+ Errno::ECONNREFUSED,
49
+ Errno::ECONNRESET,
50
+ Errno::EHOSTUNREACH,
51
+ Errno::EINVAL,
52
+ Errno::ENETUNREACH,
53
+ Errno::EPIPE,
54
+ ::HTTPX::ConnectionError => e
55
+ raise Faraday::ConnectionFailed, e
56
+ end
57
+
40
58
  def build_request(env)
41
59
  meth = env[:method]
42
60
 
@@ -44,32 +62,53 @@ module Faraday
44
62
  headers: env.request_headers,
45
63
  body: env.body,
46
64
  }
47
- [meth, env.url, request_options]
65
+ [meth.to_s.upcase, env.url, request_options]
48
66
  end
49
67
 
50
68
  def options_from_env(env)
51
- timeout_options = {
52
- connect_timeout: env.request.open_timeout,
53
- operation_timeout: env.request.timeout,
54
- }.compact
69
+ timeout_options = {}
70
+ req_opts = env.request
71
+ if (sec = request_timeout(:read, req_opts))
72
+ timeout_options[:read_timeout] = sec
73
+ end
55
74
 
56
- options = {
57
- ssl: {},
75
+ if (sec = request_timeout(:write, req_opts))
76
+ timeout_options[:write_timeout] = sec
77
+ end
78
+
79
+ if (sec = request_timeout(:open, req_opts))
80
+ timeout_options[:connect_timeout] = sec
81
+ end
82
+
83
+ {
84
+ ssl: ssl_options_from_env(env),
58
85
  timeout: timeout_options,
59
86
  }
87
+ end
88
+
89
+ if defined?(::OpenSSL)
90
+ def ssl_options_from_env(env)
91
+ ssl_options = {}
92
+
93
+ unless env.ssl.verify.nil?
94
+ ssl_options[:verify_mode] = env.ssl.verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
95
+ end
60
96
 
61
- options[:ssl][:verify_mode] = OpenSSL::SSL::VERIFY_PEER if env.ssl.verify
62
- options[:ssl][:ca_file] = env.ssl.ca_file if env.ssl.ca_file
63
- options[:ssl][:ca_path] = env.ssl.ca_path if env.ssl.ca_path
64
- options[:ssl][:cert_store] = env.ssl.cert_store if env.ssl.cert_store
65
- options[:ssl][:cert] = env.ssl.client_cert if env.ssl.client_cert
66
- options[:ssl][:key] = env.ssl.client_key if env.ssl.client_key
67
- options[:ssl][:ssl_version] = env.ssl.version if env.ssl.version
68
- options[:ssl][:verify_depth] = env.ssl.verify_depth if env.ssl.verify_depth
69
- options[:ssl][:min_version] = env.ssl.min_version if env.ssl.min_version
70
- options[:ssl][:max_version] = env.ssl.max_version if env.ssl.max_version
71
-
72
- options
97
+ ssl_options[:ca_file] = env.ssl.ca_file if env.ssl.ca_file
98
+ ssl_options[:ca_path] = env.ssl.ca_path if env.ssl.ca_path
99
+ ssl_options[:cert_store] = env.ssl.cert_store if env.ssl.cert_store
100
+ ssl_options[:cert] = env.ssl.client_cert if env.ssl.client_cert
101
+ ssl_options[:key] = env.ssl.client_key if env.ssl.client_key
102
+ ssl_options[:ssl_version] = env.ssl.version if env.ssl.version
103
+ ssl_options[:verify_depth] = env.ssl.verify_depth if env.ssl.verify_depth
104
+ ssl_options[:min_version] = env.ssl.min_version if env.ssl.min_version
105
+ ssl_options[:max_version] = env.ssl.max_version if env.ssl.max_version
106
+ ssl_options
107
+ end
108
+ else
109
+ def ssl_options_from_env(*)
110
+ {}
111
+ end
73
112
  end
74
113
  end
75
114
 
@@ -100,32 +139,17 @@ module Faraday
100
139
  end
101
140
 
102
141
  module ReasonPlugin
103
- if RUBY_VERSION < "2.5"
104
- def self.load_dependencies(*)
105
- require "webrick"
106
- end
107
- else
108
- def self.load_dependencies(*)
109
- require "net/http/status"
110
- end
142
+ def self.load_dependencies(*)
143
+ require "net/http/status"
111
144
  end
145
+
112
146
  module ResponseMethods
113
- if RUBY_VERSION < "2.5"
114
- def reason
115
- WEBrick::HTTPStatus::StatusMessage.fetch(@status)
116
- end
117
- else
118
- def reason
119
- Net::HTTP::STATUS_CODES.fetch(@status)
120
- end
147
+ def reason
148
+ Net::HTTP::STATUS_CODES.fetch(@status)
121
149
  end
122
150
  end
123
151
  end
124
152
 
125
- def self.session
126
- @session ||= ::HTTPX.plugin(:compression).plugin(:persistent).plugin(ReasonPlugin)
127
- end
128
-
129
153
  class ParallelManager
130
154
  class ResponseHandler < SimpleDelegator
131
155
  attr_reader :env
@@ -158,8 +182,9 @@ module Faraday
158
182
 
159
183
  include RequestMixin
160
184
 
161
- def initialize
185
+ def initialize(options)
162
186
  @handlers = []
187
+ @connection_options = options
163
188
  end
164
189
 
165
190
  def enqueue(request)
@@ -169,85 +194,96 @@ module Faraday
169
194
  end
170
195
 
171
196
  def run
197
+ return unless @handlers.last
198
+
172
199
  env = @handlers.last.env
173
200
 
174
- session = HTTPX.session.with(options_from_env(env))
175
- session = session.plugin(:proxy).with(proxy: { uri: env.request.proxy }) if env.request.proxy
176
- session = session.plugin(OnDataPlugin) if env.request.stream_response?
201
+ connect(env) do |session|
202
+ requests = @handlers.map { |handler| session.build_request(*build_request(handler.env)) }
177
203
 
178
- requests = @handlers.map { |handler| session.build_request(*build_request(handler.env)) }
204
+ if env.request.stream_response?
205
+ requests.each do |request|
206
+ request.response_on_data = env.request.on_data
207
+ end
208
+ end
179
209
 
180
- if env.request.stream_response?
181
- requests.each do |request|
182
- request.response_on_data = env.request.on_data
210
+ responses = session.request(*requests)
211
+ Array(responses).each_with_index do |response, index|
212
+ handler = @handlers[index]
213
+ handler.on_response.call(response)
214
+ handler.on_complete.call(handler.env)
183
215
  end
184
216
  end
217
+ rescue ::HTTPX::TimeoutError => e
218
+ raise Faraday::TimeoutError, e
219
+ end
185
220
 
186
- responses = session.request(*requests)
187
- Array(responses).each_with_index do |response, index|
188
- handler = @handlers[index]
189
- handler.on_response.call(response)
190
- handler.on_complete.call(handler.env)
191
- end
221
+ # from Faraday::Adapter#connection
222
+ def connection(env)
223
+ conn = build_connection(env)
224
+ return conn unless block_given?
225
+
226
+ yield conn
227
+ end
228
+
229
+ private
230
+
231
+ # from Faraday::Adapter#request_timeout
232
+ def request_timeout(type, options)
233
+ key = Faraday::Adapter::TIMEOUT_KEYS[type]
234
+ options[key] || options[:timeout]
192
235
  end
193
236
  end
194
237
 
195
238
  self.supports_parallel = true
196
239
 
197
240
  class << self
198
- def setup_parallel_manager
199
- ParallelManager.new
241
+ def setup_parallel_manager(options = {})
242
+ ParallelManager.new(options)
200
243
  end
201
244
  end
202
245
 
203
- def initialize(app, options = {})
204
- super(app)
205
- @session_options = options
206
- end
207
-
208
246
  def call(env)
209
247
  super
210
248
  if parallel?(env)
211
249
  handler = env[:parallel_manager].enqueue(env)
212
250
  handler.on_response do |response|
213
- response.raise_for_status
214
- save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
215
- response_headers.merge!(response.headers)
251
+ if response.is_a?(::HTTPX::Response)
252
+ save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
253
+ response_headers.merge!(response.headers)
254
+ end
255
+ else
256
+ env[:error] = response.error
257
+ save_response(env, 0, "", {}, nil)
216
258
  end
217
259
  end
218
260
  return handler
219
261
  end
220
262
 
221
- session = HTTPX.session
222
- session = session.with(@session_options) unless @session_options.empty?
223
- session = session.with(options_from_env(env))
224
- session = session.plugin(:proxy).with(proxy: { uri: env.request.proxy }) if env.request.proxy
225
- session = session.plugin(OnDataPlugin) if env.request.stream_response?
226
-
227
- request = session.build_request(*build_request(env))
228
-
229
- request.response_on_data = env.request.on_data if env.request.stream_response?
230
-
231
- response = session.request(request)
232
- response.raise_for_status unless response.is_a?(::HTTPX::Response)
263
+ response = connect_and_request(env)
233
264
  save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
234
265
  response_headers.merge!(response.headers)
235
266
  end
236
267
  @app.call(env)
237
- rescue ::HTTPX::TLSError => e
238
- raise SSL_ERROR, e
239
- rescue Errno::ECONNABORTED,
240
- Errno::ECONNREFUSED,
241
- Errno::ECONNRESET,
242
- Errno::EHOSTUNREACH,
243
- Errno::EINVAL,
244
- Errno::ENETUNREACH,
245
- Errno::EPIPE => e
246
- raise CONNECTION_FAILED_ERROR, e
247
268
  end
248
269
 
249
270
  private
250
271
 
272
+ def connect_and_request(env)
273
+ connect(env) do |session|
274
+ request = session.build_request(*build_request(env))
275
+
276
+ request.response_on_data = env.request.on_data if env.request.stream_response?
277
+
278
+ response = session.request(request)
279
+ # do not call #raise_for_status for HTTP 4xx or 5xx, as faraday has a middleware for that.
280
+ response.raise_for_status unless response.is_a?(::HTTPX::Response)
281
+ response
282
+ end
283
+ rescue ::HTTPX::TimeoutError => e
284
+ raise Faraday::TimeoutError, e
285
+ end
286
+
251
287
  def parallel?(env)
252
288
  env[:parallel_manager]
253
289
  end