httpx 1.4.0 → 1.4.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.
- 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
|