httpx 0.20.0 → 1.3.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/LICENSE.txt +0 -48
- data/README.md +54 -45
- data/doc/release_notes/0_10_0.md +2 -2
- data/doc/release_notes/0_11_0.md +3 -5
- data/doc/release_notes/0_12_0.md +5 -5
- data/doc/release_notes/0_13_0.md +5 -5
- data/doc/release_notes/0_14_0.md +2 -2
- data/doc/release_notes/0_16_0.md +3 -3
- data/doc/release_notes/0_17_0.md +1 -1
- data/doc/release_notes/0_18_0.md +4 -4
- data/doc/release_notes/0_18_2.md +1 -1
- data/doc/release_notes/0_19_0.md +1 -1
- data/doc/release_notes/0_19_8.md +1 -1
- data/doc/release_notes/0_20_0.md +2 -2
- data/doc/release_notes/0_20_1.md +5 -0
- data/doc/release_notes/0_20_2.md +7 -0
- data/doc/release_notes/0_20_3.md +6 -0
- data/doc/release_notes/0_20_4.md +17 -0
- data/doc/release_notes/0_20_5.md +3 -0
- data/doc/release_notes/0_21_0.md +96 -0
- data/doc/release_notes/0_21_1.md +12 -0
- data/doc/release_notes/0_22_0.md +13 -0
- data/doc/release_notes/0_22_1.md +11 -0
- data/doc/release_notes/0_22_2.md +5 -0
- data/doc/release_notes/0_22_3.md +55 -0
- data/doc/release_notes/0_22_4.md +6 -0
- data/doc/release_notes/0_22_5.md +6 -0
- data/doc/release_notes/0_23_0.md +42 -0
- data/doc/release_notes/0_23_1.md +5 -0
- data/doc/release_notes/0_23_2.md +5 -0
- data/doc/release_notes/0_23_3.md +6 -0
- data/doc/release_notes/0_23_4.md +5 -0
- data/doc/release_notes/0_24_0.md +48 -0
- data/doc/release_notes/0_24_1.md +12 -0
- data/doc/release_notes/0_24_2.md +12 -0
- data/doc/release_notes/0_24_3.md +12 -0
- data/doc/release_notes/0_24_4.md +18 -0
- data/doc/release_notes/0_24_5.md +6 -0
- data/doc/release_notes/0_24_6.md +5 -0
- data/doc/release_notes/0_24_7.md +10 -0
- data/doc/release_notes/1_0_0.md +60 -0
- data/doc/release_notes/1_0_1.md +5 -0
- data/doc/release_notes/1_0_2.md +7 -0
- data/doc/release_notes/1_1_0.md +32 -0
- data/doc/release_notes/1_1_1.md +17 -0
- data/doc/release_notes/1_1_2.md +12 -0
- data/doc/release_notes/1_1_3.md +18 -0
- data/doc/release_notes/1_1_4.md +6 -0
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/doc/release_notes/1_2_1.md +6 -0
- data/doc/release_notes/1_2_2.md +10 -0
- data/doc/release_notes/1_2_3.md +16 -0
- data/doc/release_notes/1_2_4.md +8 -0
- data/doc/release_notes/1_2_5.md +7 -0
- data/doc/release_notes/1_2_6.md +13 -0
- data/doc/release_notes/1_3_0.md +18 -0
- data/doc/release_notes/1_3_1.md +17 -0
- data/lib/httpx/adapters/datadog.rb +215 -122
- data/lib/httpx/adapters/faraday.rb +145 -107
- data/lib/httpx/adapters/sentry.rb +26 -7
- data/lib/httpx/adapters/webmock.rb +34 -18
- data/lib/httpx/altsvc.rb +63 -26
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +12 -0
- data/lib/httpx/callbacks.rb +5 -3
- data/lib/httpx/chainable.rb +54 -39
- data/lib/httpx/connection/http1.rb +75 -44
- data/lib/httpx/connection/http2.rb +31 -38
- data/lib/httpx/connection.rb +287 -117
- data/lib/httpx/domain_name.rb +10 -13
- data/lib/httpx/errors.rb +52 -2
- data/lib/httpx/extensions.rb +24 -131
- data/lib/httpx/io/ssl.rb +83 -77
- data/lib/httpx/io/tcp.rb +48 -71
- data/lib/httpx/io/udp.rb +18 -52
- data/lib/httpx/io/unix.rb +10 -15
- data/lib/httpx/io.rb +3 -9
- data/lib/httpx/loggable.rb +4 -19
- data/lib/httpx/options.rb +176 -118
- data/lib/httpx/parser/http1.rb +4 -0
- data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
- data/lib/httpx/plugins/{authentication → auth}/digest.rb +14 -14
- data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
- data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +4 -3
- data/lib/httpx/plugins/aws_sigv4.rb +12 -9
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
- data/lib/httpx/plugins/circuit_breaker.rb +148 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
- data/lib/httpx/plugins/cookies.rb +30 -17
- data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +14 -12
- data/lib/httpx/plugins/expect.rb +21 -14
- data/lib/httpx/plugins/follow_redirects.rb +140 -41
- data/lib/httpx/plugins/grpc/call.rb +2 -3
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
- data/lib/httpx/plugins/grpc/message.rb +7 -37
- data/lib/httpx/plugins/grpc.rb +36 -29
- data/lib/httpx/plugins/h2c.rb +26 -19
- data/lib/httpx/plugins/internal_telemetry.rb +16 -0
- data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
- data/lib/httpx/plugins/oauth.rb +175 -0
- data/lib/httpx/plugins/persistent.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +23 -13
- data/lib/httpx/plugins/proxy/socks4.rb +9 -7
- data/lib/httpx/plugins/proxy/socks5.rb +11 -9
- data/lib/httpx/plugins/proxy.rb +80 -61
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/plugins/rate_limiter.rb +5 -1
- data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
- data/lib/httpx/plugins/response_cache/store.rb +62 -25
- data/lib/httpx/plugins/response_cache.rb +105 -12
- data/lib/httpx/plugins/retries.rb +87 -17
- data/lib/httpx/plugins/ssrf_filter.rb +145 -0
- data/lib/httpx/plugins/stream.rb +27 -23
- data/lib/httpx/plugins/upgrade/h2.rb +4 -4
- data/lib/httpx/plugins/upgrade.rb +8 -10
- data/lib/httpx/plugins/webdav.rb +80 -0
- data/lib/httpx/pool/synch_pool.rb +93 -0
- data/lib/httpx/pool.rb +102 -27
- data/lib/httpx/punycode.rb +9 -291
- data/lib/httpx/request/body.rb +154 -0
- data/lib/httpx/request.rb +130 -146
- data/lib/httpx/resolver/https.rb +62 -27
- data/lib/httpx/resolver/multi.rb +9 -13
- data/lib/httpx/resolver/native.rb +192 -76
- data/lib/httpx/resolver/resolver.rb +34 -9
- data/lib/httpx/resolver/system.rb +16 -11
- data/lib/httpx/resolver.rb +38 -16
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +96 -0
- data/lib/httpx/response.rb +159 -217
- data/lib/httpx/selector.rb +9 -4
- data/lib/httpx/session.rb +137 -89
- data/lib/httpx/session_extensions.rb +4 -1
- data/lib/httpx/timers.rb +34 -8
- data/lib/httpx/transcoder/body.rb +0 -2
- data/lib/httpx/transcoder/chunker.rb +0 -1
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +52 -33
- data/lib/httpx/transcoder/gzip.rb +74 -0
- data/lib/httpx/transcoder/json.rb +21 -8
- data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
- data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +4 -4
- data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
- data/lib/httpx/transcoder/multipart.rb +17 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +72 -0
- data/lib/httpx/transcoder/utils/inflater.rb +19 -0
- data/lib/httpx/transcoder/xml.rb +52 -0
- data/lib/httpx/transcoder.rb +5 -6
- data/lib/httpx/utils.rb +36 -16
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +12 -14
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +2 -1
- data/sig/callbacks.rbs +3 -3
- data/sig/chainable.rbs +11 -9
- data/sig/connection/http1.rbs +8 -7
- data/sig/connection/http2.rbs +19 -19
- data/sig/connection.rbs +64 -24
- data/sig/errors.rbs +22 -3
- data/sig/httpx.rbs +5 -4
- data/sig/io/ssl.rbs +27 -0
- data/sig/io/tcp.rbs +60 -0
- data/sig/io/udp.rbs +20 -0
- data/sig/io/unix.rbs +27 -0
- data/sig/io.rbs +6 -0
- data/sig/options.rbs +32 -22
- data/sig/parser/http1.rbs +1 -1
- data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
- data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
- data/sig/plugins/brotli.rbs +22 -0
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/circuit_breaker.rbs +71 -0
- data/sig/plugins/compression.rbs +7 -5
- data/sig/plugins/cookies/jar.rbs +2 -2
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
- data/sig/plugins/follow_redirects.rbs +18 -4
- data/sig/plugins/grpc/call.rbs +19 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +7 -32
- data/sig/plugins/h2c.rbs +1 -1
- data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
- data/sig/plugins/oauth.rbs +54 -0
- data/sig/plugins/proxy/http.rbs +3 -0
- data/sig/plugins/proxy/socks4.rbs +9 -6
- data/sig/plugins/proxy/socks5.rbs +10 -6
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/proxy.rbs +13 -5
- data/sig/plugins/push_promise.rbs +3 -3
- data/sig/plugins/rate_limiter.rbs +1 -1
- data/sig/plugins/response_cache.rbs +36 -7
- data/sig/plugins/retries.rbs +30 -8
- data/sig/plugins/stream.rbs +24 -17
- data/sig/plugins/upgrade.rbs +5 -3
- data/sig/pool.rbs +10 -7
- data/sig/request/body.rbs +38 -0
- data/sig/request.rbs +15 -24
- data/sig/resolver/https.rbs +8 -3
- data/sig/resolver/native.rbs +17 -4
- data/sig/resolver/resolver.rbs +8 -6
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +9 -5
- data/sig/response/body.rbs +53 -0
- data/sig/response/buffer.rbs +24 -0
- data/sig/response.rbs +24 -39
- data/sig/selector.rbs +1 -1
- data/sig/session.rbs +29 -18
- data/sig/timers.rbs +18 -8
- data/sig/transcoder/body.rbs +4 -3
- data/sig/transcoder/deflate.rbs +11 -0
- data/sig/transcoder/form.rbs +5 -3
- data/sig/transcoder/gzip.rbs +24 -0
- data/sig/transcoder/json.rbs +8 -3
- data/sig/{plugins → transcoder}/multipart.rbs +15 -19
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +29 -0
- data/sig/transcoder/utils/inflater.rbs +12 -0
- data/sig/transcoder/xml.rbs +22 -0
- data/sig/transcoder.rbs +24 -9
- data/sig/utils.rbs +8 -2
- metadata +163 -41
- data/lib/httpx/plugins/authentication.rb +0 -20
- data/lib/httpx/plugins/basic_authentication.rb +0 -30
- data/lib/httpx/plugins/compression/brotli.rb +0 -54
- data/lib/httpx/plugins/compression/deflate.rb +0 -49
- data/lib/httpx/plugins/compression/gzip.rb +0 -88
- data/lib/httpx/plugins/compression.rb +0 -164
- data/lib/httpx/plugins/multipart/decoder.rb +0 -187
- data/lib/httpx/plugins/multipart.rb +0 -84
- data/lib/httpx/registry.rb +0 -85
- data/sig/plugins/authentication.rbs +0 -11
- data/sig/plugins/compression/brotli.rbs +0 -21
- data/sig/plugins/compression/deflate.rbs +0 -17
- data/sig/plugins/compression/gzip.rbs +0 -29
- data/sig/registry.rbs +0 -12
- /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thread"
|
4
|
+
|
5
|
+
module HTTPX
|
6
|
+
class SynchPool < Pool
|
7
|
+
def initialize(options)
|
8
|
+
super
|
9
|
+
|
10
|
+
@connections = ConnectionStore.new(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: #wrap
|
14
|
+
def find_or_new_connection(uri, options, &blk)
|
15
|
+
@connections.find_or_new(uri, options) do |new_conn|
|
16
|
+
catch(:coalesced) do
|
17
|
+
init_connection(new_conn, options)
|
18
|
+
blk.call(new_conn) if blk
|
19
|
+
new_conn
|
20
|
+
end
|
21
|
+
end
|
22
|
+
find_connection(uri, options) || new_connection(uri, options, &blk)
|
23
|
+
end
|
24
|
+
|
25
|
+
class ConnectionManager
|
26
|
+
include Enumerable
|
27
|
+
|
28
|
+
def initialize(limit = 3)
|
29
|
+
@connections = []
|
30
|
+
@used = 0
|
31
|
+
@limit = limit
|
32
|
+
end
|
33
|
+
|
34
|
+
def each(*args, &blk)
|
35
|
+
@connections.each(*args, &blk)
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_or_new(uri, options, &blk)
|
39
|
+
raise "over limit" if @used >= @limit
|
40
|
+
|
41
|
+
@used += 1
|
42
|
+
conn = @connections.find do |connection|
|
43
|
+
connection.match?(uri, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
if conn
|
47
|
+
@connections.delete(conn)
|
48
|
+
else
|
49
|
+
conn = options.connection_class.new(uri, options)
|
50
|
+
blk[conn]
|
51
|
+
end
|
52
|
+
|
53
|
+
conn
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ConnectionStore
|
58
|
+
include Enumerable
|
59
|
+
|
60
|
+
def initialize(options)
|
61
|
+
@connections = Hash.new { |hs, k| hs[k] ||= ConnectionManager.new }
|
62
|
+
@conn_mtx = Thread::Mutex.new
|
63
|
+
@conn_waiter = ConditionVariable.new
|
64
|
+
@timeout = Float(options.fetch(:pool_timeout, 5))
|
65
|
+
end
|
66
|
+
|
67
|
+
def each(&block)
|
68
|
+
return enum_for(__meth__) unless block
|
69
|
+
|
70
|
+
@conn_mtx.synchronize do
|
71
|
+
@connections.each_value do |conns|
|
72
|
+
conns.each(&block)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_or_new(uri, options, &blk)
|
78
|
+
@connections[uri.origin].find_or_new(uri, options, &blk)
|
79
|
+
end
|
80
|
+
|
81
|
+
# def <<(conn)
|
82
|
+
# @conn_mtx.synchronize do
|
83
|
+
# origin, conns = @connections.find { |_orig, _| conn.origins.include?(origin) }
|
84
|
+
# (conns || @connections[conn.origin.to_s]) << conn
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
|
88
|
+
def empty?
|
89
|
+
@conn_mtx.synchronize { super }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/httpx/pool.rb
CHANGED
@@ -7,7 +7,7 @@ require "httpx/resolver"
|
|
7
7
|
|
8
8
|
module HTTPX
|
9
9
|
class Pool
|
10
|
-
using ArrayExtensions
|
10
|
+
using ArrayExtensions::FilterMap
|
11
11
|
extend Forwardable
|
12
12
|
|
13
13
|
def_delegator :@timers, :after
|
@@ -17,7 +17,17 @@ module HTTPX
|
|
17
17
|
@timers = Timers.new
|
18
18
|
@selector = Selector.new
|
19
19
|
@connections = []
|
20
|
-
|
20
|
+
end
|
21
|
+
|
22
|
+
def wrap
|
23
|
+
connections = @connections
|
24
|
+
@connections = []
|
25
|
+
|
26
|
+
begin
|
27
|
+
yield self
|
28
|
+
ensure
|
29
|
+
@connections.unshift(*connections)
|
30
|
+
end
|
21
31
|
end
|
22
32
|
|
23
33
|
def empty?
|
@@ -44,16 +54,15 @@ module HTTPX
|
|
44
54
|
connection.emit(:error, e)
|
45
55
|
end
|
46
56
|
rescue Exception # rubocop:disable Lint/RescueException
|
47
|
-
@connections.each(&:
|
57
|
+
@connections.each(&:force_reset)
|
48
58
|
raise
|
49
59
|
end
|
50
60
|
|
51
61
|
def close(connections = @connections)
|
52
62
|
return if connections.empty?
|
53
63
|
|
54
|
-
@timers.cancel
|
55
64
|
connections = connections.reject(&:inflight?)
|
56
|
-
connections.each(&:
|
65
|
+
connections.each(&:terminate)
|
57
66
|
next_tick until connections.none? { |c| c.state != :idle && @connections.include?(c) }
|
58
67
|
|
59
68
|
# close resolvers
|
@@ -67,22 +76,39 @@ module HTTPX
|
|
67
76
|
resolver.close unless resolver.closed?
|
68
77
|
end
|
69
78
|
# for https resolver
|
70
|
-
resolver_connections.each(&:
|
79
|
+
resolver_connections.each(&:terminate)
|
71
80
|
next_tick until resolver_connections.none? { |c| c.state != :idle && @connections.include?(c) }
|
72
81
|
end
|
73
82
|
|
74
83
|
def init_connection(connection, _options)
|
75
|
-
resolve_connection(connection)
|
76
84
|
connection.timers = @timers
|
77
|
-
connection.on(:open) do
|
78
|
-
@connected_connections += 1
|
79
|
-
end
|
80
85
|
connection.on(:activate) do
|
81
86
|
select_connection(connection)
|
82
87
|
end
|
88
|
+
connection.on(:exhausted) do
|
89
|
+
case connection.state
|
90
|
+
when :closed
|
91
|
+
connection.idling
|
92
|
+
@connections << connection
|
93
|
+
select_connection(connection)
|
94
|
+
when :closing
|
95
|
+
connection.once(:close) do
|
96
|
+
connection.idling
|
97
|
+
@connections << connection
|
98
|
+
select_connection(connection)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
connection.on(:close) do
|
103
|
+
unregister_connection(connection)
|
104
|
+
end
|
105
|
+
connection.on(:terminate) do
|
106
|
+
unregister_connection(connection, true)
|
107
|
+
end
|
108
|
+
resolve_connection(connection) unless connection.family
|
83
109
|
end
|
84
110
|
|
85
|
-
def deactivate(connections)
|
111
|
+
def deactivate(*connections)
|
86
112
|
connections.each do |connection|
|
87
113
|
connection.deactivate
|
88
114
|
deselect_connection(connection) if connection.state == :inactive
|
@@ -94,9 +120,24 @@ module HTTPX
|
|
94
120
|
# maximize pipelining by opening as few connections as possible.
|
95
121
|
#
|
96
122
|
def find_connection(uri, options)
|
97
|
-
@connections.find do |connection|
|
123
|
+
conn = @connections.find do |connection|
|
98
124
|
connection.match?(uri, options)
|
99
125
|
end
|
126
|
+
|
127
|
+
return unless conn
|
128
|
+
|
129
|
+
case conn.state
|
130
|
+
when :closed
|
131
|
+
conn.idling
|
132
|
+
select_connection(conn)
|
133
|
+
when :closing
|
134
|
+
conn.once(:close) do
|
135
|
+
conn.idling
|
136
|
+
select_connection(conn)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
conn
|
100
141
|
end
|
101
142
|
|
102
143
|
private
|
@@ -113,19 +154,61 @@ module HTTPX
|
|
113
154
|
# resolve a name (not the same as name being an IP, yet)
|
114
155
|
# 2. when the connection is initialized with an external already open IO.
|
115
156
|
#
|
157
|
+
connection.once(:connect_error, &connection.method(:handle_error))
|
116
158
|
on_resolver_connection(connection)
|
117
159
|
return
|
118
160
|
end
|
119
161
|
|
120
162
|
find_resolver_for(connection) do |resolver|
|
121
|
-
resolver << connection
|
163
|
+
resolver << try_clone_connection(connection, resolver.family)
|
122
164
|
next if resolver.empty?
|
123
165
|
|
124
166
|
select_connection(resolver)
|
125
167
|
end
|
126
168
|
end
|
127
169
|
|
170
|
+
def try_clone_connection(connection, family)
|
171
|
+
connection.family ||= family
|
172
|
+
|
173
|
+
return connection if connection.family == family
|
174
|
+
|
175
|
+
new_connection = connection.class.new(connection.origin, connection.options)
|
176
|
+
new_connection.family = family
|
177
|
+
|
178
|
+
connection.once(:tcp_open) { new_connection.force_reset }
|
179
|
+
connection.once(:connect_error) do |err|
|
180
|
+
if new_connection.connecting?
|
181
|
+
new_connection.merge(connection)
|
182
|
+
connection.emit(:cloned, new_connection)
|
183
|
+
connection.force_reset
|
184
|
+
else
|
185
|
+
connection.__send__(:handle_error, err)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
new_connection.once(:tcp_open) do |new_conn|
|
190
|
+
if new_conn != connection
|
191
|
+
new_conn.merge(connection)
|
192
|
+
connection.force_reset
|
193
|
+
end
|
194
|
+
end
|
195
|
+
new_connection.once(:connect_error) do |err|
|
196
|
+
if connection.connecting?
|
197
|
+
# main connection has the requests
|
198
|
+
connection.merge(new_connection)
|
199
|
+
new_connection.emit(:cloned, connection)
|
200
|
+
new_connection.force_reset
|
201
|
+
else
|
202
|
+
new_connection.__send__(:handle_error, err)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
init_connection(new_connection, connection.options)
|
207
|
+
new_connection
|
208
|
+
end
|
209
|
+
|
128
210
|
def on_resolver_connection(connection)
|
211
|
+
@connections << connection unless @connections.include?(connection)
|
129
212
|
found_connection = @connections.find do |ch|
|
130
213
|
ch != connection && ch.mergeable?(connection)
|
131
214
|
end
|
@@ -142,9 +225,9 @@ module HTTPX
|
|
142
225
|
end
|
143
226
|
|
144
227
|
def on_resolver_error(connection, error)
|
228
|
+
return connection.emit(:connect_error, error) if connection.connecting? && connection.callbacks_for?(:connect_error)
|
229
|
+
|
145
230
|
connection.emit(:error, error)
|
146
|
-
# must remove connection by hand, hasn't been started yet
|
147
|
-
unregister_connection(connection)
|
148
231
|
end
|
149
232
|
|
150
233
|
def on_resolver_close(resolver)
|
@@ -158,21 +241,12 @@ module HTTPX
|
|
158
241
|
end
|
159
242
|
|
160
243
|
def register_connection(connection)
|
161
|
-
if connection.state == :open
|
162
|
-
# if open, an IO was passed upstream, therefore
|
163
|
-
# consider it connected already.
|
164
|
-
@connected_connections += 1
|
165
|
-
end
|
166
244
|
select_connection(connection)
|
167
|
-
connection.on(:close) do
|
168
|
-
unregister_connection(connection)
|
169
|
-
end
|
170
245
|
end
|
171
246
|
|
172
|
-
def unregister_connection(connection)
|
173
|
-
@connections.delete(connection)
|
247
|
+
def unregister_connection(connection, cleanup = !connection.used?)
|
248
|
+
@connections.delete(connection) if cleanup
|
174
249
|
deselect_connection(connection)
|
175
|
-
@connected_connections -= 1
|
176
250
|
end
|
177
251
|
|
178
252
|
def select_connection(connection)
|
@@ -186,6 +260,7 @@ module HTTPX
|
|
186
260
|
def coalesce_connections(conn1, conn2)
|
187
261
|
return register_connection(conn2) unless conn1.coalescable?(conn2)
|
188
262
|
|
263
|
+
conn2.emit(:tcp_open, conn1)
|
189
264
|
conn1.merge(conn2)
|
190
265
|
@connections.delete(conn2)
|
191
266
|
end
|
@@ -201,7 +276,7 @@ module HTTPX
|
|
201
276
|
def find_resolver_for(connection)
|
202
277
|
connection_options = connection.options
|
203
278
|
resolver_type = connection_options.resolver_class
|
204
|
-
resolver_type = Resolver.
|
279
|
+
resolver_type = Resolver.resolver_for(resolver_type)
|
205
280
|
|
206
281
|
@resolvers[resolver_type] ||= begin
|
207
282
|
resolver_manager = if resolver_type.multi?
|
data/lib/httpx/punycode.rb
CHANGED
@@ -1,304 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTPX
|
4
|
-
|
5
|
-
|
4
|
+
module Punycode
|
5
|
+
module_function
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
begin
|
8
|
+
require "idnx"
|
9
9
|
|
10
10
|
def encode_hostname(hostname)
|
11
11
|
Idnx.to_punycode(hostname)
|
12
12
|
end
|
13
|
-
|
14
|
-
|
15
|
-
rescue LoadError
|
16
|
-
# :nocov:
|
17
|
-
# -*- coding: utf-8 -*-
|
18
|
-
#--
|
19
|
-
# punycode.rb - PunyCode encoder for the Domain Name library
|
20
|
-
#
|
21
|
-
# Copyright (C) 2011-2017 Akinori MUSHA, All rights reserved.
|
22
|
-
#
|
23
|
-
# Ported from puny.c, a part of VeriSign XCode (encode/decode) IDN
|
24
|
-
# Library.
|
25
|
-
#
|
26
|
-
# Copyright (C) 2000-2002 Verisign Inc., All rights reserved.
|
27
|
-
#
|
28
|
-
# Redistribution and use in source and binary forms, with or
|
29
|
-
# without modification, are permitted provided that the following
|
30
|
-
# conditions are met:
|
31
|
-
#
|
32
|
-
# 1) Redistributions of source code must retain the above copyright
|
33
|
-
# notice, this list of conditions and the following disclaimer.
|
34
|
-
#
|
35
|
-
# 2) Redistributions in binary form must reproduce the above copyright
|
36
|
-
# notice, this list of conditions and the following disclaimer in
|
37
|
-
# the documentation and/or other materials provided with the
|
38
|
-
# distribution.
|
39
|
-
#
|
40
|
-
# 3) Neither the name of the VeriSign Inc. nor the names of its
|
41
|
-
# contributors may be used to endorse or promote products derived
|
42
|
-
# from this software without specific prior written permission.
|
43
|
-
#
|
44
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
45
|
-
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
46
|
-
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
47
|
-
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
48
|
-
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
49
|
-
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
50
|
-
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
51
|
-
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
52
|
-
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
53
|
-
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
54
|
-
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
55
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
56
|
-
#
|
57
|
-
# This software is licensed under the BSD open source license. For more
|
58
|
-
# information visit www.opensource.org.
|
59
|
-
#
|
60
|
-
# Authors:
|
61
|
-
# John Colosi (VeriSign)
|
62
|
-
# Srikanth Veeramachaneni (VeriSign)
|
63
|
-
# Nagesh Chigurupati (Verisign)
|
64
|
-
# Praveen Srinivasan(Verisign)
|
65
|
-
#++
|
66
|
-
module Punycode
|
67
|
-
BASE = 36
|
68
|
-
TMIN = 1
|
69
|
-
TMAX = 26
|
70
|
-
SKEW = 38
|
71
|
-
DAMP = 700
|
72
|
-
INITIAL_BIAS = 72
|
73
|
-
INITIAL_N = 0x80
|
74
|
-
DELIMITER = "-"
|
75
|
-
|
76
|
-
MAXINT = (1 << 32) - 1
|
77
|
-
|
78
|
-
LOBASE = BASE - TMIN
|
79
|
-
CUTOFF = LOBASE * TMAX / 2
|
80
|
-
|
81
|
-
RE_NONBASIC = /[^\x00-\x7f]/.freeze
|
82
|
-
|
83
|
-
# Returns the numeric value of a basic code point (for use in
|
84
|
-
# representing integers) in the range 0 to base-1, or nil if cp
|
85
|
-
# is does not represent a value.
|
86
|
-
DECODE_DIGIT = {}.tap do |map|
|
87
|
-
# ASCII A..Z map to 0..25
|
88
|
-
# ASCII a..z map to 0..25
|
89
|
-
(0..25).each { |i| map[65 + i] = map[97 + i] = i }
|
90
|
-
# ASCII 0..9 map to 26..35
|
91
|
-
(26..35).each { |i| map[22 + i] = i }
|
92
|
-
end
|
93
|
-
|
94
|
-
# Returns the basic code point whose value (when used for
|
95
|
-
# representing integers) is d, which must be in the range 0 to
|
96
|
-
# BASE-1. The lowercase form is used unless flag is true, in
|
97
|
-
# which case the uppercase form is used. The behavior is
|
98
|
-
# undefined if flag is nonzero and digit d has no uppercase
|
99
|
-
# form.
|
100
|
-
ENCODE_DIGIT = proc { |d, flag|
|
101
|
-
(d + 22 + (d < 26 ? 75 : 0) - (flag ? (1 << 5) : 0)).chr
|
102
|
-
# 0..25 map to ASCII a..z or A..Z
|
103
|
-
# 26..35 map to ASCII 0..9
|
104
|
-
}
|
105
|
-
|
106
|
-
DOT = "."
|
107
|
-
PREFIX = "xn--"
|
108
|
-
|
109
|
-
# Most errors we raise are basically kind of ArgumentError.
|
110
|
-
class ArgumentError < ::ArgumentError; end
|
111
|
-
class BufferOverflowError < ArgumentError; end
|
112
|
-
|
113
|
-
module_function
|
114
|
-
|
115
|
-
# Encode a +string+ in Punycode
|
116
|
-
def encode(string)
|
117
|
-
input = string.unpack("U*")
|
118
|
-
output = +""
|
119
|
-
|
120
|
-
# Initialize the state
|
121
|
-
n = INITIAL_N
|
122
|
-
delta = 0
|
123
|
-
bias = INITIAL_BIAS
|
124
|
-
|
125
|
-
# Handle the basic code points
|
126
|
-
input.each { |cp| output << cp.chr if cp < 0x80 }
|
127
|
-
|
128
|
-
h = b = output.length
|
129
|
-
|
130
|
-
# h is the number of code points that have been handled, b is the
|
131
|
-
# number of basic code points, and out is the number of characters
|
132
|
-
# that have been output.
|
133
|
-
|
134
|
-
output << DELIMITER if b > 0
|
135
|
-
|
136
|
-
# Main encoding loop
|
137
|
-
|
138
|
-
while h < input.length
|
139
|
-
# All non-basic code points < n have been handled already. Find
|
140
|
-
# the next larger one
|
141
|
-
|
142
|
-
m = MAXINT
|
143
|
-
input.each do |cp|
|
144
|
-
m = cp if (n...m) === cp
|
145
|
-
end
|
146
|
-
|
147
|
-
# Increase delta enough to advance the decoder's <n,i> state to
|
148
|
-
# <m,0>, but guard against overflow
|
149
|
-
|
150
|
-
delta += (m - n) * (h + 1)
|
151
|
-
raise BufferOverflowError if delta > MAXINT
|
152
|
-
|
153
|
-
n = m
|
154
|
-
|
155
|
-
input.each do |cp|
|
156
|
-
# AMC-ACE-Z can use this simplified version instead
|
157
|
-
if cp < n
|
158
|
-
delta += 1
|
159
|
-
raise BufferOverflowError if delta > MAXINT
|
160
|
-
elsif cp == n
|
161
|
-
# Represent delta as a generalized variable-length integer
|
162
|
-
q = delta
|
163
|
-
k = BASE
|
164
|
-
loop do
|
165
|
-
t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
|
166
|
-
break if q < t
|
167
|
-
|
168
|
-
q, r = (q - t).divmod(BASE - t)
|
169
|
-
output << ENCODE_DIGIT[t + r, false]
|
170
|
-
k += BASE
|
171
|
-
end
|
172
|
-
|
173
|
-
output << ENCODE_DIGIT[q, false]
|
174
|
-
|
175
|
-
# Adapt the bias
|
176
|
-
delta = h == b ? delta / DAMP : delta >> 1
|
177
|
-
delta += delta / (h + 1)
|
178
|
-
bias = 0
|
179
|
-
while delta > CUTOFF
|
180
|
-
delta /= LOBASE
|
181
|
-
bias += BASE
|
182
|
-
end
|
183
|
-
bias += (LOBASE + 1) * delta / (delta + SKEW)
|
184
|
-
|
185
|
-
delta = 0
|
186
|
-
h += 1
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
delta += 1
|
191
|
-
n += 1
|
192
|
-
end
|
193
|
-
|
194
|
-
output
|
195
|
-
end
|
196
|
-
|
197
|
-
# Encode a hostname using IDN/Punycode algorithms
|
13
|
+
rescue LoadError
|
198
14
|
def encode_hostname(hostname)
|
199
|
-
hostname.
|
200
|
-
|
201
|
-
hostname.split(DOT).map do |name|
|
202
|
-
if name.match(RE_NONBASIC)
|
203
|
-
PREFIX + encode(name)
|
204
|
-
else
|
205
|
-
name
|
206
|
-
end
|
207
|
-
end.join(DOT)
|
208
|
-
end
|
209
|
-
|
210
|
-
# Decode a +string+ encoded in Punycode
|
211
|
-
def decode(string)
|
212
|
-
# Initialize the state
|
213
|
-
n = INITIAL_N
|
214
|
-
i = 0
|
215
|
-
bias = INITIAL_BIAS
|
216
|
-
|
217
|
-
if j = string.rindex(DELIMITER)
|
218
|
-
b = string[0...j]
|
219
|
-
|
220
|
-
b.match(RE_NONBASIC) &&
|
221
|
-
raise(ArgumentError, "Illegal character is found in basic part: #{string.inspect}")
|
222
|
-
|
223
|
-
# Handle the basic code points
|
224
|
-
|
225
|
-
output = b.unpack("U*")
|
226
|
-
u = string[(j + 1)..-1]
|
227
|
-
else
|
228
|
-
output = []
|
229
|
-
u = string
|
230
|
-
end
|
231
|
-
|
232
|
-
# Main decoding loop: Start just after the last delimiter if any
|
233
|
-
# basic code points were copied; start at the beginning
|
234
|
-
# otherwise.
|
235
|
-
|
236
|
-
input = u.unpack("C*")
|
237
|
-
input_length = input.length
|
238
|
-
h = 0
|
239
|
-
out = output.length
|
240
|
-
|
241
|
-
while h < input_length
|
242
|
-
# Decode a generalized variable-length integer into delta,
|
243
|
-
# which gets added to i. The overflow checking is easier
|
244
|
-
# if we increase i as we go, then subtract off its starting
|
245
|
-
# value at the end to obtain delta.
|
246
|
-
|
247
|
-
oldi = i
|
248
|
-
w = 1
|
249
|
-
k = BASE
|
250
|
-
|
251
|
-
loop do
|
252
|
-
(digit = DECODE_DIGIT[input[h]]) ||
|
253
|
-
raise(ArgumentError, "Illegal character is found in non-basic part: #{string.inspect}")
|
254
|
-
h += 1
|
255
|
-
i += digit * w
|
256
|
-
raise BufferOverflowError if i > MAXINT
|
257
|
-
|
258
|
-
t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
|
259
|
-
break if digit < t
|
260
|
-
|
261
|
-
w *= BASE - t
|
262
|
-
raise BufferOverflowError if w > MAXINT
|
263
|
-
|
264
|
-
k += BASE
|
265
|
-
(h < input_length) || raise(ArgumentError, "Malformed input given: #{string.inspect}")
|
266
|
-
end
|
267
|
-
|
268
|
-
# Adapt the bias
|
269
|
-
delta = oldi == 0 ? i / DAMP : (i - oldi) >> 1
|
270
|
-
delta += delta / (out + 1)
|
271
|
-
bias = 0
|
272
|
-
while delta > CUTOFF
|
273
|
-
delta /= LOBASE
|
274
|
-
bias += BASE
|
275
|
-
end
|
276
|
-
bias += (LOBASE + 1) * delta / (delta + SKEW)
|
277
|
-
|
278
|
-
# i was supposed to wrap around from out+1 to 0, incrementing
|
279
|
-
# n each time, so we'll fix that now:
|
280
|
-
|
281
|
-
q, i = i.divmod(out + 1)
|
282
|
-
n += q
|
283
|
-
raise BufferOverflowError if n > MAXINT
|
284
|
-
|
285
|
-
# Insert n at position i of the output:
|
286
|
-
|
287
|
-
output[i, 0] = n
|
288
|
-
|
289
|
-
out += 1
|
290
|
-
i += 1
|
291
|
-
end
|
292
|
-
output.pack("U*")
|
293
|
-
end
|
15
|
+
warn "#{hostname} cannot be converted to punycode. Install the " \
|
16
|
+
"\"idnx\" gem: https://github.com/HoneyryderChuck/idnx"
|
294
17
|
|
295
|
-
|
296
|
-
def decode_hostname(hostname)
|
297
|
-
hostname.gsub(/(\A|#{Regexp.quote(DOT)})#{Regexp.quote(PREFIX)}([^#{Regexp.quote(DOT)}]*)/o) do
|
298
|
-
Regexp.last_match(1) << decode(Regexp.last_match(2))
|
299
|
-
end
|
18
|
+
hostname
|
300
19
|
end
|
301
20
|
end
|
302
|
-
# :nocov:
|
303
21
|
end
|
304
|
-
end
|
22
|
+
end
|