httpx 0.20.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|