httpx 1.2.3 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/doc/release_notes/1_2_4.md +8 -0
- data/doc/release_notes/1_2_5.md +7 -0
- data/lib/httpx/adapters/datadog.rb +140 -45
- data/lib/httpx/altsvc.rb +2 -2
- data/lib/httpx/connection/http1.rb +1 -1
- data/lib/httpx/connection.rb +3 -3
- data/lib/httpx/plugins/auth/digest.rb +2 -1
- data/lib/httpx/plugins/aws_sigv4.rb +2 -2
- data/lib/httpx/plugins/response_cache.rb +4 -1
- data/lib/httpx/plugins/stream.rb +10 -1
- data/lib/httpx/response.rb +5 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/response.rbs +1 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e80b08847467e6ba67e57afef2787c2c617167624ae510e255dd23998b25210e
|
4
|
+
data.tar.gz: b11ea8d914872689bc1710680c9962b0362c3bf28b62f6e7209f7e2a54a34674
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fc7994fd3b460e267bce129fdcf6536d844fd5dd9695833c4075028cf128fe954c599ee96eacdf722756c6615c703c96f2df44628e34cd1964fee9f3f92e5e9
|
7
|
+
data.tar.gz: 04c4ad1bcf05a3ee96de206a05c294f29f90fa0698fb094b982027cb09db8969a99bd7a324af23dcec18d227fe9708947211fc31ae9e4cddfb4b4360df94aa99
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# 1.2.4
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* fixed issue related to inability to buffer payload to error responses (which may happen on certain error handling situations).
|
6
|
+
* fixed recovery from a lost persistent connection leaving process due to ping being sent while still marked as inactive.
|
7
|
+
* fixed datadog integration, which was not generating new spans on retried requests (when `:retries` plugin is enabled).
|
8
|
+
* fixed splitting strings into key value pairs in cases where the value would contain a "=", such as in certain base64 payloads.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# 1.2.5
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* fix for usage of correct `last-modified` header in `response_cache` plugin.
|
6
|
+
* fix usage of decoding helper methods (i.e. `response.json`) with `response_cache` plugin.
|
7
|
+
* `stream` plugin: reverted back to yielding buffered payloads for streamed responses (broke `down` integration)
|
@@ -7,6 +7,8 @@ require "datadog/tracing/contrib/patcher"
|
|
7
7
|
module Datadog::Tracing
|
8
8
|
module Contrib
|
9
9
|
module HTTPX
|
10
|
+
DATADOG_VERSION = defined?(::DDTrace) ? ::DDTrace::VERSION : ::Datadog::VERSION
|
11
|
+
|
10
12
|
METADATA_MODULE = Datadog::Tracing::Metadata
|
11
13
|
|
12
14
|
TYPE_OUTBOUND = Datadog::Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
|
@@ -22,9 +24,10 @@ module Datadog::Tracing
|
|
22
24
|
|
23
25
|
# HTTPX Datadog Plugin
|
24
26
|
#
|
25
|
-
# Enables tracing for httpx requests.
|
26
|
-
#
|
27
|
-
#
|
27
|
+
# Enables tracing for httpx requests.
|
28
|
+
#
|
29
|
+
# A span will be created for each request transaction; the span is created lazily only when
|
30
|
+
# receiving a response, and it is fed the start time stored inside the tracer object.
|
28
31
|
#
|
29
32
|
module Plugin
|
30
33
|
class RequestTracer
|
@@ -32,82 +35,174 @@ module Datadog::Tracing
|
|
32
35
|
|
33
36
|
SPAN_REQUEST = "httpx.request"
|
34
37
|
|
38
|
+
# initializes the tracer object on the +request+.
|
35
39
|
def initialize(request)
|
36
40
|
@request = request
|
41
|
+
@start_time = nil
|
42
|
+
|
43
|
+
# request objects are reused, when already buffered requests get rerouted to a different
|
44
|
+
# connection due to connection issues, or when they already got a response, but need to
|
45
|
+
# be retried. In such situations, the original span needs to be extended for the former,
|
46
|
+
# while a new is required for the latter.
|
47
|
+
request.on(:idle) { reset }
|
48
|
+
# the span is initialized when the request is buffered in the parser, which is the closest
|
49
|
+
# one gets to actually sending the request.
|
50
|
+
request.on(:headers) { call }
|
37
51
|
end
|
38
52
|
|
39
|
-
|
40
|
-
|
53
|
+
# sets up the span start time, while preparing the on response callback.
|
54
|
+
def call(*args)
|
55
|
+
return if @start_time
|
56
|
+
|
57
|
+
start(*args)
|
58
|
+
|
59
|
+
@request.once(:response, &method(:finish))
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# just sets the span init time. It can be passed a +start_time+ in cases where
|
65
|
+
# this is collected outside the request transaction.
|
66
|
+
def start(start_time = now)
|
67
|
+
@start_time = start_time
|
68
|
+
end
|
69
|
+
|
70
|
+
# resets the start time for already finished request transactions.
|
71
|
+
def reset
|
72
|
+
return unless @start_time
|
73
|
+
|
74
|
+
start
|
75
|
+
end
|
41
76
|
|
42
|
-
|
77
|
+
# creates the span from the collected +@start_time+ to what the +response+ state
|
78
|
+
# contains. It also resets internal state to allow this object to be reused.
|
79
|
+
def finish(response)
|
80
|
+
return unless @start_time
|
81
|
+
|
82
|
+
span = initialize_span
|
43
83
|
|
84
|
+
return unless span
|
85
|
+
|
86
|
+
if response.is_a?(::HTTPX::ErrorResponse)
|
87
|
+
span.set_error(response.error)
|
88
|
+
else
|
89
|
+
span.set_tag(TAG_STATUS_CODE, response.status.to_s)
|
90
|
+
|
91
|
+
span.set_error(::HTTPX::HTTPError.new(response)) if response.status >= 400 && response.status <= 599
|
92
|
+
end
|
93
|
+
|
94
|
+
span.finish
|
95
|
+
ensure
|
96
|
+
@start_time = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
# return a span initialized with the +@request+ state.
|
100
|
+
def initialize_span
|
44
101
|
verb = @request.verb
|
45
102
|
uri = @request.uri
|
46
103
|
|
47
|
-
|
48
|
-
SPAN_REQUEST,
|
49
|
-
service: service_name(@request.uri.host, configuration, Datadog.configuration_for(self)),
|
50
|
-
span_type: TYPE_OUTBOUND
|
51
|
-
)
|
104
|
+
span = create_span(@request)
|
52
105
|
|
53
|
-
|
106
|
+
span.resource = verb
|
54
107
|
|
55
108
|
# Add additional request specific tags to the span.
|
56
109
|
|
57
|
-
|
58
|
-
|
110
|
+
span.set_tag(TAG_URL, @request.path)
|
111
|
+
span.set_tag(TAG_METHOD, verb)
|
59
112
|
|
60
|
-
|
61
|
-
|
113
|
+
span.set_tag(TAG_TARGET_HOST, uri.host)
|
114
|
+
span.set_tag(TAG_TARGET_PORT, uri.port.to_s)
|
62
115
|
|
63
116
|
# Tag as an external peer service
|
64
|
-
|
117
|
+
span.set_tag(TAG_PEER_SERVICE, span.service)
|
65
118
|
|
66
|
-
|
67
|
-
|
119
|
+
if configuration[:distributed_tracing]
|
120
|
+
propagate_trace_http(
|
121
|
+
Datadog::Tracing.active_trace.to_digest,
|
122
|
+
@request.headers
|
123
|
+
)
|
124
|
+
end
|
68
125
|
|
69
126
|
# Set analytics sample rate
|
70
|
-
if Contrib::Analytics.enabled?(
|
71
|
-
Contrib::Analytics.set_sample_rate(
|
127
|
+
if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
|
128
|
+
Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
|
72
129
|
end
|
130
|
+
|
131
|
+
span
|
73
132
|
rescue StandardError => e
|
74
133
|
Datadog.logger.error("error preparing span for http request: #{e}")
|
75
134
|
Datadog.logger.error(e.backtrace)
|
76
135
|
end
|
77
136
|
|
78
|
-
def
|
79
|
-
|
137
|
+
def now
|
138
|
+
::Datadog::Core::Utils::Time.now.utc
|
139
|
+
end
|
80
140
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@span.set_tag(TAG_STATUS_CODE, response.status.to_s)
|
141
|
+
def configuration
|
142
|
+
@configuration ||= Datadog.configuration.tracing[:httpx, @request.uri.host]
|
143
|
+
end
|
85
144
|
|
86
|
-
|
145
|
+
if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("2.0.0.beta1")
|
146
|
+
def propagate_trace_http(digest, headers)
|
147
|
+
Datadog::Tracing::Contrib::HTTP.inject(digest, headers)
|
87
148
|
end
|
88
149
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
150
|
+
def create_span(request)
|
151
|
+
Datadog::Tracing.trace(
|
152
|
+
SPAN_REQUEST,
|
153
|
+
service: service_name(request.uri.host, configuration, Datadog.configuration_for(self)),
|
154
|
+
type: TYPE_OUTBOUND,
|
155
|
+
start_time: @start_time
|
156
|
+
)
|
157
|
+
end
|
158
|
+
else
|
159
|
+
def propagate_trace_http(digest, headers)
|
160
|
+
Datadog::Tracing::Propagation::HTTP.inject!(digest, headers)
|
161
|
+
end
|
93
162
|
|
94
|
-
|
95
|
-
|
163
|
+
def create_span(request)
|
164
|
+
Datadog::Tracing.trace(
|
165
|
+
SPAN_REQUEST,
|
166
|
+
service: service_name(request.uri.host, configuration, Datadog.configuration_for(self)),
|
167
|
+
span_type: TYPE_OUTBOUND,
|
168
|
+
start_time: @start_time
|
169
|
+
)
|
170
|
+
end
|
96
171
|
end
|
97
172
|
end
|
98
173
|
|
99
174
|
module RequestMethods
|
100
|
-
|
101
|
-
|
175
|
+
# intercepts request initialization to inject the tracing logic.
|
176
|
+
def initialize(*)
|
177
|
+
super
|
102
178
|
|
103
|
-
|
104
|
-
|
179
|
+
return unless Datadog::Tracing.enabled?
|
180
|
+
|
181
|
+
RequestTracer.new(self)
|
105
182
|
end
|
106
183
|
end
|
107
184
|
|
108
185
|
module ConnectionMethods
|
109
|
-
|
110
|
-
|
186
|
+
attr_reader :init_time
|
187
|
+
|
188
|
+
def initialize(*)
|
189
|
+
super
|
190
|
+
|
191
|
+
@init_time = ::Datadog::Core::Utils::Time.now.utc
|
192
|
+
end
|
193
|
+
|
194
|
+
# handles the case when the +error+ happened during name resolution, which meanns
|
195
|
+
# that the tracing logic hasn't been injected yet; in such cases, the approximate
|
196
|
+
# initial resolving time is collected from the connection, and used as span start time,
|
197
|
+
# and the tracing object in inserted before the on response callback is called.
|
198
|
+
def handle_error(error)
|
199
|
+
return super unless Datadog::Tracing.enabled?
|
200
|
+
|
201
|
+
return super unless error.respond_to?(:connection)
|
202
|
+
|
203
|
+
@pending.each do |request|
|
204
|
+
RequestTracer.new(request).call(error.connection.init_time)
|
205
|
+
end
|
111
206
|
|
112
207
|
super
|
113
208
|
end
|
@@ -126,7 +221,7 @@ module Datadog::Tracing
|
|
126
221
|
option :distributed_tracing, default: true
|
127
222
|
option :split_by_domain, default: false
|
128
223
|
|
129
|
-
if Gem::Version.new(
|
224
|
+
if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("1.13.0")
|
130
225
|
option :enabled do |o|
|
131
226
|
o.type :bool
|
132
227
|
o.env "DD_TRACE_HTTPX_ENABLED"
|
@@ -169,25 +264,25 @@ module Datadog::Tracing
|
|
169
264
|
"httpx"
|
170
265
|
)
|
171
266
|
end
|
172
|
-
o.lazy unless Gem::Version.new(
|
267
|
+
o.lazy unless Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("1.13.0")
|
173
268
|
end
|
174
269
|
else
|
175
270
|
option :service_name do |o|
|
176
271
|
o.default do
|
177
272
|
ENV.fetch("DD_TRACE_HTTPX_SERVICE_NAME", "httpx")
|
178
273
|
end
|
179
|
-
o.lazy unless Gem::Version.new(
|
274
|
+
o.lazy unless Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("1.13.0")
|
180
275
|
end
|
181
276
|
end
|
182
277
|
|
183
278
|
option :distributed_tracing, default: true
|
184
279
|
|
185
|
-
if Gem::Version.new(
|
280
|
+
if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("1.15.0")
|
186
281
|
option :error_handler do |o|
|
187
282
|
o.type :proc
|
188
283
|
o.default_proc(&DEFAULT_ERROR_HANDLER)
|
189
284
|
end
|
190
|
-
elsif Gem::Version.new(
|
285
|
+
elsif Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("1.13.0")
|
191
286
|
option :error_handler do |o|
|
192
287
|
o.type :proc
|
193
288
|
o.experimental_default_proc(&DEFAULT_ERROR_HANDLER)
|
data/lib/httpx/altsvc.rb
CHANGED
@@ -131,9 +131,9 @@ module HTTPX
|
|
131
131
|
scanner.skip(/;/)
|
132
132
|
break if scanner.eos? || scanner.scan(/ *, */)
|
133
133
|
end
|
134
|
-
alt_params = Hash[alt_params.map { |field| field.split("=") }]
|
134
|
+
alt_params = Hash[alt_params.map { |field| field.split("=", 2) }]
|
135
135
|
|
136
|
-
alt_proto, alt_authority = alt_service.split("=")
|
136
|
+
alt_proto, alt_authority = alt_service.split("=", 2)
|
137
137
|
alt_origin = parse_altsvc_origin(alt_proto, alt_authority)
|
138
138
|
return unless alt_origin
|
139
139
|
|
data/lib/httpx/connection.rb
CHANGED
@@ -48,10 +48,10 @@ module HTTPX
|
|
48
48
|
attr_accessor :family
|
49
49
|
|
50
50
|
def initialize(uri, options)
|
51
|
-
@origins = [uri.origin]
|
52
|
-
@origin = Utils.to_uri(uri.origin)
|
53
51
|
@options = Options.new(options)
|
54
52
|
@type = initialize_type(uri, @options)
|
53
|
+
@origins = [uri.origin]
|
54
|
+
@origin = Utils.to_uri(uri.origin)
|
55
55
|
@window_size = @options.window_size
|
56
56
|
@read_buffer = Buffer.new(@options.buffer_size)
|
57
57
|
@write_buffer = Buffer.new(@options.buffer_size)
|
@@ -241,8 +241,8 @@ module HTTPX
|
|
241
241
|
# for such cases, we want to ping for availability before deciding to shovel requests.
|
242
242
|
log(level: 3) { "keep alive timeout expired, pinging connection..." }
|
243
243
|
@pending << request
|
244
|
-
parser.ping
|
245
244
|
transition(:active) if @state == :inactive
|
245
|
+
parser.ping
|
246
246
|
return
|
247
247
|
end
|
248
248
|
|
@@ -30,7 +30,8 @@ module HTTPX
|
|
30
30
|
auth_info = authenticate[/^(\w+) (.*)/, 2]
|
31
31
|
|
32
32
|
params = auth_info.split(/ *, */)
|
33
|
-
.to_h { |val| val.split("="
|
33
|
+
.to_h { |val| val.split("=", 2) }
|
34
|
+
.transform_values { |v| v.delete("\"") }
|
34
35
|
nonce = params["nonce"]
|
35
36
|
nc = next_nonce
|
36
37
|
|
@@ -197,8 +197,8 @@ module HTTPX
|
|
197
197
|
params.each.with_index.sort do |a, b|
|
198
198
|
a, a_offset = a
|
199
199
|
b, b_offset = b
|
200
|
-
a_name, a_value = a.split("=")
|
201
|
-
b_name, b_value = b.split("=")
|
200
|
+
a_name, a_value = a.split("=", 2)
|
201
|
+
b_name, b_value = b.split("=", 2)
|
202
202
|
if a_name == b_name
|
203
203
|
if a_value == b_value
|
204
204
|
a_offset <=> b_offset
|
@@ -40,7 +40,7 @@ module HTTPX
|
|
40
40
|
# the Range and Content-Range headers MUST NOT cache 206 (Partial
|
41
41
|
# Content) responses.
|
42
42
|
response.status != 206 && (
|
43
|
-
response.headers.key?("etag") || response.headers.key?("last-modified
|
43
|
+
response.headers.key?("etag") || response.headers.key?("last-modified") || response.fresh?
|
44
44
|
)
|
45
45
|
end
|
46
46
|
|
@@ -102,6 +102,9 @@ module HTTPX
|
|
102
102
|
|
103
103
|
module ResponseMethods
|
104
104
|
def copy_from_cached(other)
|
105
|
+
# 304 responses do not have content-type, which are needed for decoding.
|
106
|
+
@headers = @headers.class.new(other.headers.merge(@headers))
|
107
|
+
|
105
108
|
@body = other.body.dup
|
106
109
|
|
107
110
|
@body.rewind
|
data/lib/httpx/plugins/stream.rb
CHANGED
@@ -16,9 +16,18 @@ module HTTPX
|
|
16
16
|
begin
|
17
17
|
@on_chunk = block
|
18
18
|
|
19
|
+
if @request.response
|
20
|
+
# if we've already started collecting the payload, yield it first
|
21
|
+
# before proceeding.
|
22
|
+
body = @request.response.body
|
23
|
+
|
24
|
+
body.each do |chunk|
|
25
|
+
on_chunk(chunk)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
19
29
|
response.raise_for_status
|
20
30
|
ensure
|
21
|
-
response.close if @response
|
22
31
|
@on_chunk = nil
|
23
32
|
end
|
24
33
|
end
|
data/lib/httpx/response.rb
CHANGED
data/lib/httpx/version.rb
CHANGED
data/sig/response.rbs
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2-next
|
@@ -141,6 +141,8 @@ extra_rdoc_files:
|
|
141
141
|
- doc/release_notes/1_2_1.md
|
142
142
|
- doc/release_notes/1_2_2.md
|
143
143
|
- doc/release_notes/1_2_3.md
|
144
|
+
- doc/release_notes/1_2_4.md
|
145
|
+
- doc/release_notes/1_2_5.md
|
144
146
|
files:
|
145
147
|
- LICENSE.txt
|
146
148
|
- README.md
|
@@ -253,6 +255,8 @@ files:
|
|
253
255
|
- doc/release_notes/1_2_1.md
|
254
256
|
- doc/release_notes/1_2_2.md
|
255
257
|
- doc/release_notes/1_2_3.md
|
258
|
+
- doc/release_notes/1_2_4.md
|
259
|
+
- doc/release_notes/1_2_5.md
|
256
260
|
- lib/httpx.rb
|
257
261
|
- lib/httpx/adapters/datadog.rb
|
258
262
|
- lib/httpx/adapters/faraday.rb
|