httpx 1.4.0 → 1.4.1
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_4_1.md +19 -0
- data/lib/httpx/adapters/datadog.rb +55 -83
- data/lib/httpx/adapters/webmock.rb +7 -1
- data/lib/httpx/connection/http2.rb +1 -1
- data/lib/httpx/connection.rb +78 -20
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -0
- data/lib/httpx/request/body.rb +9 -14
- data/lib/httpx/request.rb +6 -0
- data/lib/httpx/resolver/https.rb +4 -2
- data/lib/httpx/resolver/native.rb +36 -18
- data/lib/httpx/resolver/resolver.rb +12 -6
- data/lib/httpx/resolver/system.rb +3 -5
- data/lib/httpx/selector.rb +0 -4
- data/lib/httpx/session.rb +14 -38
- data/lib/httpx/transcoder/body.rb +15 -31
- data/lib/httpx/transcoder/multipart/part.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +1 -1
- data/sig/connection.rbs +16 -1
- data/sig/request/body.rbs +0 -8
- data/sig/resolver/native.rbs +2 -0
- data/sig/session.rbs +2 -0
- data/sig/transcoder/body.rbs +1 -3
- data/sig/transcoder/utils/body_reader.rbs +1 -1
- data/sig/transcoder/utils/deflater.rbs +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8a0ae955506767cc7b2f0a8134dd920bc937f88aeba2f72f691489e4d2199ab
|
4
|
+
data.tar.gz: 96503529f27fddf76b41250f3a7a0686c93d9d6c1091255275ed1d5b2beada11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d16170323b9f5d016f496e848be6b6fb50046402ca644c2a016d05fe1af4a5b045a400b31530c8fa38f85a61351125062edce01bd04c975bc45c3205545aef71
|
7
|
+
data.tar.gz: a800e8814449fcf2d3149aadc5f335f458f7dc7fa537d7c676d8460b6bfcfd1a9cc0a96f3350b9492846fea563fca15605f228ef539b39ef36985df05431d193
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# 1.4.1
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* several `datadog` integration bugfixes
|
6
|
+
* only load the `datadog` integration when the `datadog` sdk is loaded (and not other gems that may define the `Datadog` module, like `dogstatsd`)
|
7
|
+
* do not trace if datadog integration is loaded but disabled
|
8
|
+
* distributed headers are now sent along (when the configuration is enabled, which it is by default)
|
9
|
+
* fix for handling multiple `GOAWAY` frames coming from the server (node.js servers seem to send multiple frames on connection timeout)
|
10
|
+
* fix regression for when a url is used with `httpx` which is not `http://` or `https://` (should raise `HTTPX::UnsupportedSchemaError`)
|
11
|
+
* worked around `IO.copy_stream` which was emitting incorrect bytes for HTTP/2 requests which bodies larger than the maximum supported frame size.
|
12
|
+
* multipart requests: make sure that a body declared as `Pathname` is opened for reading in binary mode.
|
13
|
+
* `webmock` integration: ensure that request events are emitted (such as plugins and integrations relying in it, such as `datadog` and the OTel integration)
|
14
|
+
* native resolver: do not propagate successful name resolutions for connections which were already closed.
|
15
|
+
* native resolver: fixed name resolution stalling, in a multi-request to multi-origin scenario, when a resolution timeout would happen.
|
16
|
+
|
17
|
+
## Chore
|
18
|
+
|
19
|
+
* refactor of the happy eyeballs and connection coalescing logic to not rely on callbacks, and instead on instance variable management (makes code more straightforward to read).
|
@@ -27,62 +27,54 @@ module Datadog::Tracing
|
|
27
27
|
# Enables tracing for httpx requests.
|
28
28
|
#
|
29
29
|
# A span will be created for each request transaction; the span is created lazily only when
|
30
|
-
#
|
30
|
+
# buffering a request, and it is fed the start time stored inside the tracer object.
|
31
31
|
#
|
32
32
|
module Plugin
|
33
|
-
|
34
|
-
|
33
|
+
module RequestTracer
|
34
|
+
extend Contrib::HttpAnnotationHelper
|
35
|
+
|
36
|
+
module_function
|
35
37
|
|
36
38
|
SPAN_REQUEST = "httpx.request"
|
37
39
|
|
38
|
-
# initializes
|
39
|
-
def
|
40
|
-
|
41
|
-
|
40
|
+
# initializes tracing on the +request+.
|
41
|
+
def call(request)
|
42
|
+
return unless configuration(request).enabled
|
43
|
+
|
44
|
+
span = nil
|
42
45
|
|
43
46
|
# request objects are reused, when already buffered requests get rerouted to a different
|
44
47
|
# connection due to connection issues, or when they already got a response, but need to
|
45
48
|
# be retried. In such situations, the original span needs to be extended for the former,
|
46
49
|
# while a new is required for the latter.
|
47
|
-
request.on(:idle)
|
50
|
+
request.on(:idle) do
|
51
|
+
span = nil
|
52
|
+
end
|
48
53
|
# the span is initialized when the request is buffered in the parser, which is the closest
|
49
54
|
# one gets to actually sending the request.
|
50
|
-
request.on(:headers)
|
51
|
-
|
52
|
-
|
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)
|
55
|
+
request.on(:headers) do
|
56
|
+
next if span
|
58
57
|
|
59
|
-
|
60
|
-
|
58
|
+
span = initialize_span(request, now)
|
59
|
+
end
|
61
60
|
|
62
|
-
|
61
|
+
request.on(:response) do |response|
|
62
|
+
unless span
|
63
|
+
next unless response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection)
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
# handles the case when the +error+ happened during name resolution, which means
|
66
|
+
# that the tracing start point hasn't been triggered yet; in such cases, the approximate
|
67
|
+
# initial resolving time is collected from the connection, and used as span start time,
|
68
|
+
# and the tracing object in inserted before the on response callback is called.
|
69
|
+
span = initialize_span(request, response.error.connection.init_time)
|
69
70
|
|
70
|
-
|
71
|
-
def reset
|
72
|
-
return unless @start_time
|
71
|
+
end
|
73
72
|
|
74
|
-
|
73
|
+
finish(response, span)
|
74
|
+
end
|
75
75
|
end
|
76
76
|
|
77
|
-
|
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
|
83
|
-
|
84
|
-
return unless span
|
85
|
-
|
77
|
+
def finish(response, span)
|
86
78
|
if response.is_a?(::HTTPX::ErrorResponse)
|
87
79
|
span.set_error(response.error)
|
88
80
|
else
|
@@ -92,40 +84,40 @@ module Datadog::Tracing
|
|
92
84
|
end
|
93
85
|
|
94
86
|
span.finish
|
95
|
-
ensure
|
96
|
-
@start_time = nil
|
97
87
|
end
|
98
88
|
|
99
89
|
# return a span initialized with the +@request+ state.
|
100
|
-
def initialize_span
|
101
|
-
verb =
|
102
|
-
uri =
|
90
|
+
def initialize_span(request, start_time)
|
91
|
+
verb = request.verb
|
92
|
+
uri = request.uri
|
93
|
+
|
94
|
+
config = configuration(request)
|
103
95
|
|
104
|
-
span = create_span(
|
96
|
+
span = create_span(request, config, start_time)
|
105
97
|
|
106
98
|
span.resource = verb
|
107
99
|
|
108
100
|
# Add additional request specific tags to the span.
|
109
101
|
|
110
|
-
span.set_tag(TAG_URL,
|
102
|
+
span.set_tag(TAG_URL, request.path)
|
111
103
|
span.set_tag(TAG_METHOD, verb)
|
112
104
|
|
113
105
|
span.set_tag(TAG_TARGET_HOST, uri.host)
|
114
|
-
span.set_tag(TAG_TARGET_PORT, uri.port
|
106
|
+
span.set_tag(TAG_TARGET_PORT, uri.port)
|
115
107
|
|
116
108
|
# Tag as an external peer service
|
117
109
|
span.set_tag(TAG_PEER_SERVICE, span.service)
|
118
110
|
|
119
|
-
if
|
111
|
+
if config[:distributed_tracing]
|
120
112
|
propagate_trace_http(
|
121
|
-
Datadog::Tracing.active_trace
|
122
|
-
|
113
|
+
Datadog::Tracing.active_trace,
|
114
|
+
request.headers
|
123
115
|
)
|
124
116
|
end
|
125
117
|
|
126
118
|
# Set analytics sample rate
|
127
|
-
if Contrib::Analytics.enabled?(
|
128
|
-
Contrib::Analytics.set_sample_rate(span,
|
119
|
+
if Contrib::Analytics.enabled?(config[:analytics_enabled])
|
120
|
+
Contrib::Analytics.set_sample_rate(span, config[:analytics_sample_rate])
|
129
121
|
end
|
130
122
|
|
131
123
|
span
|
@@ -138,34 +130,34 @@ module Datadog::Tracing
|
|
138
130
|
::Datadog::Core::Utils::Time.now.utc
|
139
131
|
end
|
140
132
|
|
141
|
-
def configuration
|
142
|
-
|
133
|
+
def configuration(request)
|
134
|
+
Datadog.configuration.tracing[:httpx, request.uri.host]
|
143
135
|
end
|
144
136
|
|
145
137
|
if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("2.0.0")
|
146
|
-
def propagate_trace_http(
|
147
|
-
Datadog::Tracing::Contrib::HTTP.inject(
|
138
|
+
def propagate_trace_http(trace, headers)
|
139
|
+
Datadog::Tracing::Contrib::HTTP.inject(trace, headers)
|
148
140
|
end
|
149
141
|
|
150
|
-
def create_span(request)
|
142
|
+
def create_span(request, configuration, start_time)
|
151
143
|
Datadog::Tracing.trace(
|
152
144
|
SPAN_REQUEST,
|
153
|
-
service: service_name(request.uri.host, configuration
|
145
|
+
service: service_name(request.uri.host, configuration),
|
154
146
|
type: TYPE_OUTBOUND,
|
155
|
-
start_time:
|
147
|
+
start_time: start_time
|
156
148
|
)
|
157
149
|
end
|
158
150
|
else
|
159
|
-
def propagate_trace_http(
|
160
|
-
Datadog::Tracing::Propagation::HTTP.inject!(
|
151
|
+
def propagate_trace_http(trace, headers)
|
152
|
+
Datadog::Tracing::Propagation::HTTP.inject!(trace.to_digest, headers)
|
161
153
|
end
|
162
154
|
|
163
|
-
def create_span(request)
|
155
|
+
def create_span(request, configuration, start_time)
|
164
156
|
Datadog::Tracing.trace(
|
165
157
|
SPAN_REQUEST,
|
166
|
-
service: service_name(request.uri.host, configuration
|
158
|
+
service: service_name(request.uri.host, configuration),
|
167
159
|
span_type: TYPE_OUTBOUND,
|
168
|
-
start_time:
|
160
|
+
start_time: start_time
|
169
161
|
)
|
170
162
|
end
|
171
163
|
end
|
@@ -178,7 +170,7 @@ module Datadog::Tracing
|
|
178
170
|
|
179
171
|
return unless Datadog::Tracing.enabled?
|
180
172
|
|
181
|
-
RequestTracer.
|
173
|
+
RequestTracer.call(self)
|
182
174
|
end
|
183
175
|
end
|
184
176
|
|
@@ -190,26 +182,6 @@ module Datadog::Tracing
|
|
190
182
|
|
191
183
|
@init_time = ::Datadog::Core::Utils::Time.now.utc
|
192
184
|
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, request = nil)
|
199
|
-
return super unless Datadog::Tracing.enabled?
|
200
|
-
|
201
|
-
return super unless error.respond_to?(:connection)
|
202
|
-
|
203
|
-
@pending.each do |req|
|
204
|
-
next if request and request == req
|
205
|
-
|
206
|
-
RequestTracer.new(req).call(error.connection.init_time)
|
207
|
-
end
|
208
|
-
|
209
|
-
RequestTracer.new(request).call(error.connection.init_time) if request
|
210
|
-
|
211
|
-
super
|
212
|
-
end
|
213
185
|
end
|
214
186
|
end
|
215
187
|
|
@@ -59,7 +59,9 @@ module WebMock
|
|
59
59
|
|
60
60
|
connection.once(:unmock_connection) do
|
61
61
|
unless connection.addresses
|
62
|
-
|
62
|
+
# reset Happy Eyeballs, fail early
|
63
|
+
connection.sibling = nil
|
64
|
+
|
63
65
|
deselect_connection(connection, selector)
|
64
66
|
end
|
65
67
|
resolve_connection(connection, selector)
|
@@ -114,6 +116,10 @@ module WebMock
|
|
114
116
|
response = Plugin.build_from_webmock_response(request, mock_response)
|
115
117
|
WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, request_signature, mock_response)
|
116
118
|
log { "mocking #{request.uri} with #{mock_response.inspect}" }
|
119
|
+
request.transition(:headers)
|
120
|
+
request.transition(:body)
|
121
|
+
request.transition(:trailers)
|
122
|
+
request.transition(:done)
|
117
123
|
request.response = response
|
118
124
|
request.emit(:response, response)
|
119
125
|
response << mock_response.body.dup unless response.is_a?(HTTPX::ErrorResponse)
|
data/lib/httpx/connection.rb
CHANGED
@@ -41,15 +41,17 @@ module HTTPX
|
|
41
41
|
|
42
42
|
def_delegator :@write_buffer, :empty?
|
43
43
|
|
44
|
-
attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session
|
44
|
+
attr_reader :type, :io, :origin, :origins, :state, :pending, :options, :ssl_session, :sibling
|
45
45
|
|
46
|
-
attr_writer :current_selector
|
46
|
+
attr_writer :current_selector
|
47
47
|
|
48
48
|
attr_accessor :current_session, :family
|
49
49
|
|
50
|
+
protected :sibling
|
51
|
+
|
50
52
|
def initialize(uri, options)
|
51
|
-
@current_session = @current_selector = @coalesced_connection = nil
|
52
|
-
@exhausted = @cloned = false
|
53
|
+
@current_session = @current_selector = @sibling = @coalesced_connection = nil
|
54
|
+
@exhausted = @cloned = @main_sibling = false
|
53
55
|
|
54
56
|
@options = Options.new(options)
|
55
57
|
@type = initialize_type(uri, @options)
|
@@ -70,9 +72,6 @@ module HTTPX
|
|
70
72
|
else
|
71
73
|
transition(:idle)
|
72
74
|
end
|
73
|
-
on(:activate) do
|
74
|
-
@current_session.select_connection(self, @current_selector)
|
75
|
-
end
|
76
75
|
on(:close) do
|
77
76
|
next if @exhausted # it'll reset
|
78
77
|
|
@@ -86,10 +85,13 @@ module HTTPX
|
|
86
85
|
on(:terminate) do
|
87
86
|
next if @exhausted # it'll reset
|
88
87
|
|
88
|
+
current_session = @current_session
|
89
|
+
current_selector = @current_selector
|
90
|
+
|
89
91
|
# may be called after ":close" above, so after the connection has been checked back in.
|
90
|
-
next unless
|
92
|
+
next unless current_session && current_selector
|
91
93
|
|
92
|
-
|
94
|
+
current_session.deselect_connection(self, current_selector)
|
93
95
|
end
|
94
96
|
|
95
97
|
on(:altsvc) do |alt_origin, origin, alt_params|
|
@@ -194,6 +196,12 @@ module HTTPX
|
|
194
196
|
end
|
195
197
|
end
|
196
198
|
|
199
|
+
def io_connected?
|
200
|
+
return @coalesced_connection.io_connected? if @coalesced_connection
|
201
|
+
|
202
|
+
@io && @io.state == :connected
|
203
|
+
end
|
204
|
+
|
197
205
|
def connecting?
|
198
206
|
@state == :idle
|
199
207
|
end
|
@@ -342,6 +350,35 @@ module HTTPX
|
|
342
350
|
on_error(error)
|
343
351
|
end
|
344
352
|
|
353
|
+
def coalesced_connection=(connection)
|
354
|
+
@coalesced_connection = connection
|
355
|
+
|
356
|
+
close_sibling
|
357
|
+
connection.merge(self)
|
358
|
+
end
|
359
|
+
|
360
|
+
def sibling=(connection)
|
361
|
+
@sibling = connection
|
362
|
+
|
363
|
+
return unless connection
|
364
|
+
|
365
|
+
@main_sibling = connection.sibling.nil?
|
366
|
+
|
367
|
+
return unless @main_sibling
|
368
|
+
|
369
|
+
connection.sibling = self
|
370
|
+
end
|
371
|
+
|
372
|
+
def handle_connect_error(error)
|
373
|
+
@connect_error = error
|
374
|
+
|
375
|
+
return handle_error(error) unless @sibling && @sibling.connecting?
|
376
|
+
|
377
|
+
@sibling.merge(self)
|
378
|
+
|
379
|
+
force_reset(true)
|
380
|
+
end
|
381
|
+
|
345
382
|
private
|
346
383
|
|
347
384
|
def connect
|
@@ -531,20 +568,22 @@ module HTTPX
|
|
531
568
|
@exhausted = true
|
532
569
|
current_session = @current_session
|
533
570
|
current_selector = @current_selector
|
534
|
-
|
535
|
-
|
571
|
+
begin
|
572
|
+
parser.close
|
573
|
+
@pending.concat(parser.pending)
|
574
|
+
ensure
|
575
|
+
@current_session = current_session
|
576
|
+
@current_selector = current_selector
|
577
|
+
end
|
578
|
+
|
536
579
|
case @state
|
537
580
|
when :closed
|
538
581
|
idling
|
539
582
|
@exhausted = false
|
540
|
-
@current_session = current_session
|
541
|
-
@current_selector = current_selector
|
542
583
|
when :closing
|
543
|
-
once(:
|
584
|
+
once(:closed) do
|
544
585
|
idling
|
545
586
|
@exhausted = false
|
546
|
-
@current_session = current_session
|
547
|
-
@current_selector = current_selector
|
548
587
|
end
|
549
588
|
end
|
550
589
|
end
|
@@ -613,13 +652,13 @@ module HTTPX
|
|
613
652
|
# connect errors, exit gracefully
|
614
653
|
error = ConnectionError.new(e.message)
|
615
654
|
error.set_backtrace(e.backtrace)
|
616
|
-
|
655
|
+
handle_connect_error(error) if connecting?
|
617
656
|
@state = :closed
|
618
657
|
disconnect
|
619
658
|
rescue TLSError, ::HTTP2::Error::ProtocolError, ::HTTP2::Error::HandshakeError => e
|
620
659
|
# connect errors, exit gracefully
|
621
660
|
handle_error(e)
|
622
|
-
|
661
|
+
handle_connect_error(e) if connecting?
|
623
662
|
@state = :closed
|
624
663
|
disconnect
|
625
664
|
end
|
@@ -634,7 +673,7 @@ module HTTPX
|
|
634
673
|
return if @state == :closed
|
635
674
|
|
636
675
|
@io.connect
|
637
|
-
|
676
|
+
close_sibling if @io.state == :connected
|
638
677
|
|
639
678
|
return unless @io.connected?
|
640
679
|
|
@@ -667,6 +706,7 @@ module HTTPX
|
|
667
706
|
|
668
707
|
purge_after_closed
|
669
708
|
disconnect if @pending.empty?
|
709
|
+
|
670
710
|
when :already_open
|
671
711
|
nextstate = :open
|
672
712
|
# the first check for given io readiness must still use a timeout.
|
@@ -677,11 +717,29 @@ module HTTPX
|
|
677
717
|
return unless @state == :inactive
|
678
718
|
|
679
719
|
nextstate = :open
|
680
|
-
|
720
|
+
|
721
|
+
# activate
|
722
|
+
@current_session.select_connection(self, @current_selector)
|
681
723
|
end
|
682
724
|
@state = nextstate
|
683
725
|
end
|
684
726
|
|
727
|
+
def close_sibling
|
728
|
+
return unless @sibling
|
729
|
+
|
730
|
+
if @sibling.io_connected?
|
731
|
+
reset
|
732
|
+
# TODO: transition connection to closed
|
733
|
+
end
|
734
|
+
|
735
|
+
unless @sibling.state == :closed
|
736
|
+
merge(@sibling) unless @main_sibling
|
737
|
+
@sibling.force_reset(true)
|
738
|
+
end
|
739
|
+
|
740
|
+
@sibling = nil
|
741
|
+
end
|
742
|
+
|
685
743
|
def purge_after_closed
|
686
744
|
@io.close if @io
|
687
745
|
@read_buffer.clear
|
data/lib/httpx/request/body.rb
CHANGED
@@ -52,7 +52,11 @@ module HTTPX
|
|
52
52
|
|
53
53
|
body = stream(@body)
|
54
54
|
if body.respond_to?(:read)
|
55
|
-
|
55
|
+
while (chunk = body.read(16_384))
|
56
|
+
block.call(chunk)
|
57
|
+
end
|
58
|
+
# TODO: use copy_stream once bug is resolved: https://bugs.ruby-lang.org/issues/21131
|
59
|
+
# ::IO.copy_stream(body, ProcIO.new(block))
|
56
60
|
elsif body.respond_to?(:each)
|
57
61
|
body.each(&block)
|
58
62
|
else
|
@@ -60,6 +64,10 @@ module HTTPX
|
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
67
|
+
def close
|
68
|
+
@body.close if @body.respond_to?(:close)
|
69
|
+
end
|
70
|
+
|
63
71
|
# if the +@body+ is rewindable, it rewinnds it.
|
64
72
|
def rewind
|
65
73
|
return if empty?
|
@@ -142,17 +150,4 @@ module HTTPX
|
|
142
150
|
end
|
143
151
|
end
|
144
152
|
end
|
145
|
-
|
146
|
-
# Wrapper yielder which can be used with functions which expect an IO writer.
|
147
|
-
class ProcIO
|
148
|
-
def initialize(block)
|
149
|
-
@block = block
|
150
|
-
end
|
151
|
-
|
152
|
-
# Implementation the IO write protocol, which yield the given chunk to +@block+.
|
153
|
-
def write(data)
|
154
|
-
@block.call(data.dup)
|
155
|
-
data.bytesize
|
156
|
-
end
|
157
|
-
end
|
158
153
|
end
|
data/lib/httpx/request.rb
CHANGED
@@ -11,6 +11,8 @@ module HTTPX
|
|
11
11
|
include Callbacks
|
12
12
|
using URIExtensions
|
13
13
|
|
14
|
+
ALLOWED_URI_SCHEMES = %w[https http].freeze
|
15
|
+
|
14
16
|
# default value used for "user-agent" header, when not overridden.
|
15
17
|
USER_AGENT = "httpx.rb/#{VERSION}".freeze # rubocop:disable Style/RedundantFreeze
|
16
18
|
|
@@ -92,6 +94,8 @@ module HTTPX
|
|
92
94
|
@uri = origin.merge("#{base_path}#{@uri}")
|
93
95
|
end
|
94
96
|
|
97
|
+
raise UnsupportedSchemeError, "#{@uri}: #{@uri.scheme}: unsupported URI scheme" unless ALLOWED_URI_SCHEMES.include?(@uri.scheme)
|
98
|
+
|
95
99
|
@state = :idle
|
96
100
|
@response = nil
|
97
101
|
@peer_address = nil
|
@@ -263,6 +267,8 @@ module HTTPX
|
|
263
267
|
return unless @state == :body
|
264
268
|
when :done
|
265
269
|
return if @state == :expect
|
270
|
+
|
271
|
+
@body.close
|
266
272
|
end
|
267
273
|
@state = nextstate
|
268
274
|
emit(@state, self)
|
data/lib/httpx/resolver/https.rb
CHANGED
@@ -82,7 +82,9 @@ module HTTPX
|
|
82
82
|
|
83
83
|
if hostname.nil?
|
84
84
|
hostname = connection.peer.host
|
85
|
-
log
|
85
|
+
log do
|
86
|
+
"resolver #{FAMILY_TYPES[@record_type]}: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}"
|
87
|
+
end if connection.peer.non_ascii_hostname
|
86
88
|
|
87
89
|
hostname = @resolver.generate_candidates(hostname).each do |name|
|
88
90
|
@queries[name.to_s] = connection
|
@@ -90,7 +92,7 @@ module HTTPX
|
|
90
92
|
else
|
91
93
|
@queries[hostname] = connection
|
92
94
|
end
|
93
|
-
log { "resolver
|
95
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: query for #{hostname}" }
|
94
96
|
|
95
97
|
begin
|
96
98
|
request = build_request(hostname)
|
@@ -63,7 +63,10 @@ module HTTPX
|
|
63
63
|
@ns_index += 1
|
64
64
|
nameserver = @nameserver
|
65
65
|
if nameserver && @ns_index < nameserver.size
|
66
|
-
log
|
66
|
+
log do
|
67
|
+
"resolver #{FAMILY_TYPES[@record_type]}: " \
|
68
|
+
"failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})"
|
69
|
+
end
|
67
70
|
transition(:idle)
|
68
71
|
@timeouts.clear
|
69
72
|
else
|
@@ -140,17 +143,22 @@ module HTTPX
|
|
140
143
|
|
141
144
|
return unless timeout <= 0
|
142
145
|
|
146
|
+
elapsed_after = @_timeouts[@_timeouts.size - @timeouts[host].size]
|
143
147
|
@timeouts[host].shift
|
144
148
|
|
145
149
|
if !@timeouts[host].empty?
|
146
|
-
log
|
150
|
+
log do
|
151
|
+
"resolver #{FAMILY_TYPES[@record_type]}: timeout after #{elapsed_after}s, retry (with #{@timeouts[host].first}s) #{host}..."
|
152
|
+
end
|
147
153
|
# must downgrade to tcp AND retry on same host as last
|
148
154
|
downgrade_socket
|
149
155
|
resolve(connection, h)
|
150
156
|
elsif @ns_index + 1 < @nameserver.size
|
151
157
|
# try on the next nameserver
|
152
158
|
@ns_index += 1
|
153
|
-
log
|
159
|
+
log do
|
160
|
+
"resolver #{FAMILY_TYPES[@record_type]}: failed resolving #{host} on nameserver #{@nameserver[@ns_index - 1]} (timeout error)"
|
161
|
+
end
|
154
162
|
transition(:idle)
|
155
163
|
@timeouts.clear
|
156
164
|
resolve(connection, h)
|
@@ -167,7 +175,8 @@ module HTTPX
|
|
167
175
|
ex = ResolveTimeoutError.new(loop_time, "Timed out while resolving #{connection.peer.host}")
|
168
176
|
ex.set_backtrace(ex ? ex.backtrace : caller)
|
169
177
|
emit_resolve_error(connection, host, ex)
|
170
|
-
|
178
|
+
|
179
|
+
close_or_resolve
|
171
180
|
end
|
172
181
|
end
|
173
182
|
|
@@ -249,15 +258,15 @@ module HTTPX
|
|
249
258
|
hostname, connection = @queries.first
|
250
259
|
reset_hostname(hostname, reset_candidates: false)
|
251
260
|
|
252
|
-
|
261
|
+
if @queries.value?(connection)
|
262
|
+
resolve
|
263
|
+
else
|
253
264
|
@connections.delete(connection)
|
254
265
|
ex = NativeResolveError.new(connection, connection.peer.host, "name or service not known")
|
255
266
|
ex.set_backtrace(ex ? ex.backtrace : caller)
|
256
267
|
emit_resolve_error(connection, connection.peer.host, ex)
|
257
|
-
|
268
|
+
close_or_resolve
|
258
269
|
end
|
259
|
-
|
260
|
-
resolve
|
261
270
|
when :message_truncated
|
262
271
|
# TODO: what to do if it's already tcp??
|
263
272
|
return if @socket_type == :tcp
|
@@ -326,7 +335,7 @@ module HTTPX
|
|
326
335
|
transition(:idle)
|
327
336
|
transition(:open)
|
328
337
|
end
|
329
|
-
log { "resolver: ALIAS #{hostname_alias} for #{name}" }
|
338
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: ALIAS #{hostname_alias} for #{name}" }
|
330
339
|
resolve(connection, hostname_alias)
|
331
340
|
return
|
332
341
|
end
|
@@ -338,9 +347,7 @@ module HTTPX
|
|
338
347
|
catch(:coalesced) { emit_addresses(connection, @family, addresses.map { |addr| addr["data"] }) }
|
339
348
|
end
|
340
349
|
end
|
341
|
-
|
342
|
-
|
343
|
-
resolve
|
350
|
+
close_or_resolve
|
344
351
|
end
|
345
352
|
|
346
353
|
def resolve(connection = @connections.first, hostname = nil)
|
@@ -352,7 +359,10 @@ module HTTPX
|
|
352
359
|
|
353
360
|
if hostname.nil?
|
354
361
|
hostname = connection.peer.host
|
355
|
-
log
|
362
|
+
log do
|
363
|
+
"resolver #{FAMILY_TYPES[@record_type]}: " \
|
364
|
+
"resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}"
|
365
|
+
end if connection.peer.non_ascii_hostname
|
356
366
|
|
357
367
|
hostname = generate_candidates(hostname).each do |name|
|
358
368
|
@queries[name] = connection
|
@@ -360,14 +370,14 @@ module HTTPX
|
|
360
370
|
else
|
361
371
|
@queries[hostname] = connection
|
362
372
|
end
|
363
|
-
log { "resolver
|
373
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: query for #{hostname}" }
|
364
374
|
begin
|
365
375
|
@write_buffer << encode_dns_query(hostname)
|
366
376
|
rescue Resolv::DNS::EncodeError => e
|
367
377
|
reset_hostname(hostname, connection: connection)
|
368
378
|
@connections.delete(connection)
|
369
379
|
emit_resolve_error(connection, hostname, e)
|
370
|
-
|
380
|
+
close_or_resolve
|
371
381
|
end
|
372
382
|
end
|
373
383
|
|
@@ -397,10 +407,10 @@ module HTTPX
|
|
397
407
|
|
398
408
|
case @socket_type
|
399
409
|
when :udp
|
400
|
-
log { "resolver: server: udp://#{ip}:#{port}..." }
|
410
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: server: udp://#{ip}:#{port}..." }
|
401
411
|
UDP.new(ip, port, @options)
|
402
412
|
when :tcp
|
403
|
-
log { "resolver: server: tcp://#{ip}:#{port}..." }
|
413
|
+
log { "resolver #{FAMILY_TYPES[@record_type]}: server: tcp://#{ip}:#{port}..." }
|
404
414
|
origin = URI("tcp://#{ip}:#{port}")
|
405
415
|
TCP.new(origin, [ip], @options)
|
406
416
|
end
|
@@ -463,7 +473,7 @@ module HTTPX
|
|
463
473
|
emit_resolve_error(connection, host, error)
|
464
474
|
end
|
465
475
|
end
|
466
|
-
|
476
|
+
close_or_resolve
|
467
477
|
end
|
468
478
|
|
469
479
|
def reset_hostname(hostname, connection: @queries.delete(hostname), reset_candidates: true)
|
@@ -478,5 +488,13 @@ module HTTPX
|
|
478
488
|
# reset timeouts
|
479
489
|
@timeouts.delete_if { |h, _| candidates.include?(h) }
|
480
490
|
end
|
491
|
+
|
492
|
+
def close_or_resolve
|
493
|
+
if @connections.empty?
|
494
|
+
emit(:close, self)
|
495
|
+
else
|
496
|
+
resolve
|
497
|
+
end
|
498
|
+
end
|
481
499
|
end
|
482
500
|
end
|
@@ -72,17 +72,21 @@ module HTTPX
|
|
72
72
|
# double emission check, but allow early resolution to work
|
73
73
|
return if !early_resolve && connection.addresses && !addresses.intersect?(connection.addresses)
|
74
74
|
|
75
|
-
log
|
75
|
+
log do
|
76
|
+
"resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: " \
|
77
|
+
"answer #{FAMILY_TYPES[RECORD_TYPES[family]]} #{connection.peer.host}: #{addresses.inspect}"
|
78
|
+
end
|
79
|
+
|
76
80
|
if @current_selector && # if triggered by early resolve, session may not be here yet
|
77
81
|
!connection.io &&
|
78
82
|
connection.options.ip_families.size > 1 &&
|
79
83
|
family == Socket::AF_INET &&
|
80
84
|
addresses.first.to_s != connection.peer.host.to_s
|
81
|
-
log { "resolver:
|
85
|
+
log { "resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: applying resolution delay..." }
|
86
|
+
|
82
87
|
@current_selector.after(0.05) do
|
83
|
-
|
84
|
-
|
85
|
-
(connection.addresses && addresses.intersect?(connection.addresses))
|
88
|
+
# double emission check
|
89
|
+
unless connection.addresses && addresses.intersect?(connection.addresses)
|
86
90
|
emit_resolved_connection(connection, addresses, early_resolve)
|
87
91
|
end
|
88
92
|
end
|
@@ -97,6 +101,8 @@ module HTTPX
|
|
97
101
|
begin
|
98
102
|
connection.addresses = addresses
|
99
103
|
|
104
|
+
return if connection.state == :closed
|
105
|
+
|
100
106
|
emit(:resolve, connection)
|
101
107
|
rescue StandardError => e
|
102
108
|
if early_resolve
|
@@ -146,7 +152,7 @@ module HTTPX
|
|
146
152
|
end
|
147
153
|
|
148
154
|
def emit_connection_error(connection, error)
|
149
|
-
return connection.
|
155
|
+
return connection.handle_connect_error(error) if connection.connecting?
|
150
156
|
|
151
157
|
connection.emit(:error, error)
|
152
158
|
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "forwardable"
|
4
3
|
require "resolv"
|
5
4
|
|
6
5
|
module HTTPX
|
7
6
|
class Resolver::System < Resolver::Resolver
|
8
7
|
using URIExtensions
|
9
|
-
extend Forwardable
|
10
8
|
|
11
9
|
RESOLV_ERRORS = [Resolv::ResolvError,
|
12
10
|
Resolv::DNS::Requester::RequestError,
|
@@ -24,8 +22,6 @@ module HTTPX
|
|
24
22
|
|
25
23
|
attr_reader :state
|
26
24
|
|
27
|
-
def_delegator :@connections, :empty?
|
28
|
-
|
29
25
|
def initialize(options)
|
30
26
|
super(nil, options)
|
31
27
|
@resolver_options = @options.resolver_options
|
@@ -162,7 +158,9 @@ module HTTPX
|
|
162
158
|
|
163
159
|
hostname = connection.peer.host
|
164
160
|
scheme = connection.origin.scheme
|
165
|
-
log
|
161
|
+
log do
|
162
|
+
"resolver: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}"
|
163
|
+
end if connection.peer.non_ascii_hostname
|
166
164
|
|
167
165
|
transition(:open)
|
168
166
|
|
data/lib/httpx/selector.rb
CHANGED
data/lib/httpx/session.rb
CHANGED
@@ -69,8 +69,6 @@ module HTTPX
|
|
69
69
|
while (connection = @pool.pop_connection)
|
70
70
|
next if connection.state == :closed
|
71
71
|
|
72
|
-
connection.current_session = self
|
73
|
-
connection.current_selector = selector
|
74
72
|
select_connection(connection, selector)
|
75
73
|
end
|
76
74
|
begin
|
@@ -126,9 +124,15 @@ module HTTPX
|
|
126
124
|
end
|
127
125
|
|
128
126
|
def select_connection(connection, selector)
|
127
|
+
pin_connection(connection, selector)
|
129
128
|
selector.register(connection)
|
130
129
|
end
|
131
130
|
|
131
|
+
def pin_connection(connection, selector)
|
132
|
+
connection.current_session = self
|
133
|
+
connection.current_selector = selector
|
134
|
+
end
|
135
|
+
|
132
136
|
alias_method :select_resolver, :select_connection
|
133
137
|
|
134
138
|
def deselect_connection(connection, selector, cloned = false)
|
@@ -160,36 +164,8 @@ module HTTPX
|
|
160
164
|
new_connection = connection.class.new(connection.origin, connection.options)
|
161
165
|
|
162
166
|
new_connection.family = family
|
163
|
-
new_connection.current_session = self
|
164
|
-
new_connection.current_selector = selector
|
165
|
-
|
166
|
-
connection.once(:tcp_open) { new_connection.force_reset(true) }
|
167
|
-
connection.once(:connect_error) do |err|
|
168
|
-
if new_connection.connecting?
|
169
|
-
new_connection.merge(connection)
|
170
|
-
connection.emit(:cloned, new_connection)
|
171
|
-
connection.force_reset(true)
|
172
|
-
else
|
173
|
-
connection.__send__(:handle_error, err)
|
174
|
-
end
|
175
|
-
end
|
176
167
|
|
177
|
-
|
178
|
-
if new_conn != connection
|
179
|
-
new_conn.merge(connection)
|
180
|
-
connection.force_reset(true)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
new_connection.once(:connect_error) do |err|
|
184
|
-
if connection.connecting?
|
185
|
-
# main connection has the requests
|
186
|
-
connection.merge(new_connection)
|
187
|
-
new_connection.emit(:cloned, connection)
|
188
|
-
new_connection.force_reset(true)
|
189
|
-
else
|
190
|
-
new_connection.__send__(:handle_error, err)
|
191
|
-
end
|
192
|
-
end
|
168
|
+
connection.sibling = new_connection
|
193
169
|
|
194
170
|
do_init_connection(new_connection, selector)
|
195
171
|
new_connection
|
@@ -203,14 +179,15 @@ module HTTPX
|
|
203
179
|
|
204
180
|
connection = @pool.checkout_connection(request_uri, options)
|
205
181
|
|
206
|
-
connection.current_session = self
|
207
|
-
connection.current_selector = selector
|
208
|
-
|
209
182
|
case connection.state
|
210
183
|
when :idle
|
211
184
|
do_init_connection(connection, selector)
|
212
185
|
when :open
|
213
|
-
|
186
|
+
if options.io
|
187
|
+
select_connection(connection, selector)
|
188
|
+
else
|
189
|
+
pin_connection(connection, selector)
|
190
|
+
end
|
214
191
|
when :closed
|
215
192
|
connection.idling
|
216
193
|
select_connection(connection, selector)
|
@@ -219,6 +196,8 @@ module HTTPX
|
|
219
196
|
connection.idling
|
220
197
|
select_connection(connection, selector)
|
221
198
|
end
|
199
|
+
else
|
200
|
+
pin_connection(connection, selector)
|
222
201
|
end
|
223
202
|
|
224
203
|
connection
|
@@ -372,7 +351,6 @@ module HTTPX
|
|
372
351
|
# resolve a name (not the same as name being an IP, yet)
|
373
352
|
# 2. when the connection is initialized with an external already open IO.
|
374
353
|
#
|
375
|
-
connection.once(:connect_error, &connection.method(:handle_error))
|
376
354
|
on_resolver_connection(connection, selector)
|
377
355
|
return
|
378
356
|
end
|
@@ -428,8 +406,6 @@ module HTTPX
|
|
428
406
|
return false
|
429
407
|
end
|
430
408
|
|
431
|
-
conn2.emit(:tcp_open, conn1)
|
432
|
-
conn1.merge(conn2)
|
433
409
|
conn2.coalesced_connection = conn1
|
434
410
|
select_connection(conn1, selector) if from_pool
|
435
411
|
deselect_connection(conn2, selector)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "delegate"
|
4
4
|
|
5
5
|
module HTTPX::Transcoder
|
6
6
|
module Body
|
@@ -8,48 +8,32 @@ module HTTPX::Transcoder
|
|
8
8
|
|
9
9
|
module_function
|
10
10
|
|
11
|
-
class Encoder
|
12
|
-
extend Forwardable
|
13
|
-
|
14
|
-
def_delegator :@raw, :to_s
|
15
|
-
|
16
|
-
def_delegator :@raw, :==
|
17
|
-
|
11
|
+
class Encoder < SimpleDelegator
|
18
12
|
def initialize(body)
|
19
|
-
|
13
|
+
body = body.open(File::RDONLY, encoding: Encoding::BINARY) if Object.const_defined?(:Pathname) && body.is_a?(Pathname)
|
14
|
+
@body = body
|
15
|
+
super(body)
|
20
16
|
end
|
21
17
|
|
22
18
|
def bytesize
|
23
|
-
if @
|
24
|
-
@
|
25
|
-
elsif @
|
26
|
-
@
|
27
|
-
elsif @
|
28
|
-
@
|
29
|
-
elsif @
|
30
|
-
@
|
31
|
-
elsif @
|
19
|
+
if @body.respond_to?(:bytesize)
|
20
|
+
@body.bytesize
|
21
|
+
elsif @body.respond_to?(:to_ary)
|
22
|
+
@body.sum(&:bytesize)
|
23
|
+
elsif @body.respond_to?(:size)
|
24
|
+
@body.size || Float::INFINITY
|
25
|
+
elsif @body.respond_to?(:length)
|
26
|
+
@body.length || Float::INFINITY
|
27
|
+
elsif @body.respond_to?(:each)
|
32
28
|
Float::INFINITY
|
33
29
|
else
|
34
|
-
raise Error, "cannot determine size of body: #{@
|
30
|
+
raise Error, "cannot determine size of body: #{@body.inspect}"
|
35
31
|
end
|
36
32
|
end
|
37
33
|
|
38
34
|
def content_type
|
39
35
|
"application/octet-stream"
|
40
36
|
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def respond_to_missing?(meth, *args)
|
45
|
-
@raw.respond_to?(meth, *args) || super
|
46
|
-
end
|
47
|
-
|
48
|
-
def method_missing(meth, *args, &block)
|
49
|
-
return super unless @raw.respond_to?(meth)
|
50
|
-
|
51
|
-
@raw.__send__(meth, *args, &block)
|
52
|
-
end
|
53
37
|
end
|
54
38
|
|
55
39
|
def encode(body)
|
@@ -19,7 +19,7 @@ module HTTPX
|
|
19
19
|
value = value[:body]
|
20
20
|
end
|
21
21
|
|
22
|
-
value = value.open(File::RDONLY) if Object.const_defined?(:Pathname) && value.is_a?(Pathname)
|
22
|
+
value = value.open(File::RDONLY, encoding: Encoding::BINARY) if Object.const_defined?(:Pathname) && value.is_a?(Pathname)
|
23
23
|
|
24
24
|
if value.respond_to?(:path) && value.respond_to?(:read)
|
25
25
|
# either a File, a Tempfile, or something else which has to quack like a file
|
data/lib/httpx/version.rb
CHANGED
data/lib/httpx.rb
CHANGED
@@ -61,6 +61,6 @@ require "httpx/session_extensions"
|
|
61
61
|
|
62
62
|
# load integrations when possible
|
63
63
|
|
64
|
-
require "httpx/adapters/datadog" if defined?(DDTrace) || defined?(Datadog)
|
64
|
+
require "httpx/adapters/datadog" if defined?(DDTrace) || defined?(Datadog::Tracing)
|
65
65
|
require "httpx/adapters/sentry" if defined?(Sentry)
|
66
66
|
require "httpx/adapters/webmock" if defined?(WebMock)
|
data/sig/connection.rbs
CHANGED
@@ -26,11 +26,12 @@ module HTTPX
|
|
26
26
|
attr_reader pending: Array[Request]
|
27
27
|
attr_reader options: Options
|
28
28
|
attr_reader ssl_session: OpenSSL::SSL::Session?
|
29
|
+
attr_reader sibling: instance?
|
29
30
|
attr_writer current_selector: Selector?
|
30
|
-
attr_writer coalesced_connection: instance?
|
31
31
|
attr_accessor current_session: Session?
|
32
32
|
attr_accessor family: Integer?
|
33
33
|
|
34
|
+
|
34
35
|
@window_size: Integer
|
35
36
|
@read_buffer: Buffer
|
36
37
|
@write_buffer: Buffer
|
@@ -45,6 +46,10 @@ module HTTPX
|
|
45
46
|
@intervals: Array[Timers::Interval]
|
46
47
|
@exhausted: bool
|
47
48
|
@cloned: bool
|
49
|
+
@coalesced_connection: instance?
|
50
|
+
@sibling: instance?
|
51
|
+
@main_sibling: bool
|
52
|
+
|
48
53
|
|
49
54
|
def addresses: () -> Array[ipaddr]?
|
50
55
|
|
@@ -70,6 +75,8 @@ module HTTPX
|
|
70
75
|
|
71
76
|
def connecting?: () -> bool
|
72
77
|
|
78
|
+
def io_connected?: () -> bool
|
79
|
+
|
73
80
|
def inflight?: () -> boolish
|
74
81
|
|
75
82
|
def interests: () -> io_interests?
|
@@ -98,6 +105,12 @@ module HTTPX
|
|
98
105
|
|
99
106
|
def handle_socket_timeout: (Numeric interval) -> void
|
100
107
|
|
108
|
+
def coalesced_connection=: (instance connection) -> void
|
109
|
+
|
110
|
+
def sibling=: (instance? connection) -> void
|
111
|
+
|
112
|
+
def handle_connect_error: (StandardError error) -> void
|
113
|
+
|
101
114
|
private
|
102
115
|
|
103
116
|
def initialize: (http_uri uri, Options options) -> void
|
@@ -134,6 +147,8 @@ module HTTPX
|
|
134
147
|
|
135
148
|
def handle_error: (StandardError error, ?Request? request) -> void
|
136
149
|
|
150
|
+
def close_sibling: () -> void
|
151
|
+
|
137
152
|
def purge_after_closed: () -> void
|
138
153
|
|
139
154
|
def set_request_timeouts: (Request request) -> void
|
data/sig/request/body.rbs
CHANGED
@@ -31,12 +31,4 @@ module HTTPX
|
|
31
31
|
|
32
32
|
def self.initialize_deflater_body: (body_encoder body, Encoding | String encoding) -> body_encoder
|
33
33
|
end
|
34
|
-
|
35
|
-
class ProcIO
|
36
|
-
@block: ^(String) -> void
|
37
|
-
|
38
|
-
def initialize: (^(String) -> void) -> untyped
|
39
|
-
|
40
|
-
def write: (String data) -> Integer
|
41
|
-
end
|
42
34
|
end
|
data/sig/resolver/native.rbs
CHANGED
data/sig/session.rbs
CHANGED
@@ -25,6 +25,8 @@ module HTTPX
|
|
25
25
|
|
26
26
|
def select_connection: (Connection connection, Selector selector) -> void
|
27
27
|
|
28
|
+
def pin_connection: (Resolver::Resolver | Connection connection, Selector selector) -> void
|
29
|
+
|
28
30
|
def deselect_connection: (Connection connection, Selector selector, ?bool cloned) -> void
|
29
31
|
|
30
32
|
def select_resolver: (Resolver::Native | Resolver::HTTPS resolver, Selector selector) -> void
|
data/sig/transcoder/body.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.4.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-2
|
@@ -150,6 +150,7 @@ extra_rdoc_files:
|
|
150
150
|
- doc/release_notes/1_3_3.md
|
151
151
|
- doc/release_notes/1_3_4.md
|
152
152
|
- doc/release_notes/1_4_0.md
|
153
|
+
- doc/release_notes/1_4_1.md
|
153
154
|
files:
|
154
155
|
- LICENSE.txt
|
155
156
|
- README.md
|
@@ -271,6 +272,7 @@ files:
|
|
271
272
|
- doc/release_notes/1_3_3.md
|
272
273
|
- doc/release_notes/1_3_4.md
|
273
274
|
- doc/release_notes/1_4_0.md
|
275
|
+
- doc/release_notes/1_4_1.md
|
274
276
|
- lib/httpx.rb
|
275
277
|
- lib/httpx/adapters/datadog.rb
|
276
278
|
- lib/httpx/adapters/faraday.rb
|