httpx 0.19.1 → 0.19.4
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/0_19_2.md +7 -0
- data/doc/release_notes/0_19_3.md +6 -0
- data/doc/release_notes/0_19_4.md +13 -0
- data/lib/httpx/adapters/webmock.rb +1 -0
- data/lib/httpx/io.rb +3 -16
- data/lib/httpx/plugins/retries.rb +1 -1
- data/lib/httpx/pool.rb +1 -1
- data/lib/httpx/resolver/native.rb +5 -1
- data/lib/httpx/resolver/resolver.rb +5 -2
- data/lib/httpx/version.rb +1 -1
- data/sig/resolver/native.rbs +2 -2
- metadata +8 -6
- data/lib/httpx/io/tls/box.rb +0 -365
- data/lib/httpx/io/tls/context.rb +0 -199
- data/lib/httpx/io/tls/ffi.rb +0 -390
- data/lib/httpx/io/tls.rb +0 -218
data/lib/httpx/io/tls.rb
DELETED
@@ -1,218 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "openssl"
|
4
|
-
|
5
|
-
module HTTPX
|
6
|
-
class TLS < TCP
|
7
|
-
class Error < StandardError; end
|
8
|
-
|
9
|
-
def initialize(_, _, options)
|
10
|
-
super
|
11
|
-
@encrypted = Buffer.new(Connection::BUFFER_SIZE)
|
12
|
-
@decrypted = "".b
|
13
|
-
tls_options = convert_tls_options(options.ssl)
|
14
|
-
@sni_hostname = tls_options[:hostname]
|
15
|
-
@ctx = TLS::Box.new(false, self, tls_options)
|
16
|
-
@state = :negotiated if @keep_open
|
17
|
-
end
|
18
|
-
|
19
|
-
def interests
|
20
|
-
@interests || super
|
21
|
-
end
|
22
|
-
|
23
|
-
def protocol
|
24
|
-
@protocol || super
|
25
|
-
end
|
26
|
-
|
27
|
-
def connected?
|
28
|
-
@state == :negotiated
|
29
|
-
end
|
30
|
-
|
31
|
-
def connect
|
32
|
-
super
|
33
|
-
if @keep_open
|
34
|
-
@state = :negotiated
|
35
|
-
return
|
36
|
-
end
|
37
|
-
return if @state == :negotiated ||
|
38
|
-
@state != :connected
|
39
|
-
|
40
|
-
super
|
41
|
-
@ctx.start
|
42
|
-
@interests = :r
|
43
|
-
read(@options.window_size, @decrypted)
|
44
|
-
end
|
45
|
-
|
46
|
-
# :nocov:
|
47
|
-
def inspect
|
48
|
-
id = @io.closed? ? "closed" : @io
|
49
|
-
"#<TLS(fd: #{id}): #{@ip}:#{@port} state: #{@state}>"
|
50
|
-
end
|
51
|
-
# :nocov:
|
52
|
-
|
53
|
-
alias_method :transport_close, :close
|
54
|
-
def close
|
55
|
-
transport_close
|
56
|
-
@ctx.cleanup
|
57
|
-
end
|
58
|
-
|
59
|
-
def read(*, buffer)
|
60
|
-
ret = super
|
61
|
-
return ret if !ret || ret.zero?
|
62
|
-
|
63
|
-
@ctx.decrypt(buffer.to_s.dup)
|
64
|
-
buffer.replace(@decrypted)
|
65
|
-
@decrypted.clear
|
66
|
-
buffer.bytesize
|
67
|
-
end
|
68
|
-
|
69
|
-
alias_method :unencrypted_write, :write
|
70
|
-
def write(buffer)
|
71
|
-
@ctx.encrypt(buffer.to_s.dup)
|
72
|
-
buffer.clear
|
73
|
-
do_write
|
74
|
-
end
|
75
|
-
|
76
|
-
# TLS callback.
|
77
|
-
#
|
78
|
-
# buffers the encrypted +data+
|
79
|
-
def transmit_cb(data)
|
80
|
-
log { "TLS encrypted: #{data.bytesize} bytes" }
|
81
|
-
log(level: 2) { data.inspect }
|
82
|
-
@encrypted << data
|
83
|
-
do_write
|
84
|
-
end
|
85
|
-
|
86
|
-
# TLS callback.
|
87
|
-
#
|
88
|
-
# buffers the decrypted +data+
|
89
|
-
def dispatch_cb(data)
|
90
|
-
log { "TLS decrypted: #{data.bytesize} bytes" }
|
91
|
-
log(level: 2) { data.inspect }
|
92
|
-
|
93
|
-
@decrypted << data
|
94
|
-
end
|
95
|
-
|
96
|
-
# TLS callback.
|
97
|
-
#
|
98
|
-
# signals TLS invalid status / shutdown.
|
99
|
-
def close_cb(msg = nil)
|
100
|
-
log { "TLS Error: #{msg}, closing" }
|
101
|
-
raise Error, "certificate verify failed (#{msg})"
|
102
|
-
end
|
103
|
-
|
104
|
-
# TLS callback.
|
105
|
-
#
|
106
|
-
# alpn protocol negotiation (+protocol+).
|
107
|
-
#
|
108
|
-
def alpn_protocol_cb(protocol)
|
109
|
-
@protocol = protocol
|
110
|
-
log { "TLS ALPN protocol negotiated: #{@protocol}" }
|
111
|
-
end
|
112
|
-
|
113
|
-
# TLS callback.
|
114
|
-
#
|
115
|
-
# handshake finished.
|
116
|
-
#
|
117
|
-
def handshake_cb
|
118
|
-
log { "TLS handshake completed" }
|
119
|
-
transition(:negotiated)
|
120
|
-
end
|
121
|
-
|
122
|
-
# TLS callback.
|
123
|
-
#
|
124
|
-
# passed the peer +cert+ to be verified.
|
125
|
-
#
|
126
|
-
def verify_cb(cert)
|
127
|
-
raise Error, "Peer verification enabled, but no certificate received." if cert.nil?
|
128
|
-
|
129
|
-
log { "TLS verifying #{cert}" }
|
130
|
-
@peer_cert = OpenSSL::X509::Certificate.new(cert)
|
131
|
-
|
132
|
-
# by default one doesn't verify client certificates in the server
|
133
|
-
verify_hostname(@sni_hostname)
|
134
|
-
end
|
135
|
-
|
136
|
-
# copied from:
|
137
|
-
# https://github.com/ruby/ruby/blob/8cbf2dae5aadfa5d6241b0df2bf44d55db46704f/ext/openssl/lib/openssl/ssl.rb#L395-L409
|
138
|
-
#
|
139
|
-
def verify_hostname(host)
|
140
|
-
return false unless @ctx.verify_peer && @peer_cert
|
141
|
-
|
142
|
-
OpenSSL::SSL.verify_certificate_identity(@peer_cert, host)
|
143
|
-
end
|
144
|
-
|
145
|
-
private
|
146
|
-
|
147
|
-
def do_write
|
148
|
-
nwritten = 0
|
149
|
-
until @encrypted.empty?
|
150
|
-
siz = unencrypted_write(@encrypted)
|
151
|
-
break unless !siz || siz.zero?
|
152
|
-
|
153
|
-
nwritten += siz
|
154
|
-
end
|
155
|
-
nwritten
|
156
|
-
end
|
157
|
-
|
158
|
-
def convert_tls_options(ssl_options)
|
159
|
-
options = {}
|
160
|
-
options[:verify_peer] = !ssl_options.key?(:verify_mode) || ssl_options[:verify_mode] != OpenSSL::SSL::VERIFY_NONE
|
161
|
-
options[:version] = ssl_options[:ssl_version] if ssl_options.key?(:ssl_version)
|
162
|
-
|
163
|
-
if ssl_options.key?(:key)
|
164
|
-
private_key = ssl_options[:key]
|
165
|
-
private_key = private_key.to_pem if private_key.respond_to?(:to_pem)
|
166
|
-
options[:private_key] = private_key
|
167
|
-
end
|
168
|
-
|
169
|
-
if ssl_options.key?(:ca_path) || ssl_options.key?(:ca_file)
|
170
|
-
ca_path = ssl_options[:ca_path] || ssl_options[:ca_file].path
|
171
|
-
options[:cert_chain] = ca_path
|
172
|
-
end
|
173
|
-
|
174
|
-
options[:ciphers] = ssl_options[:ciphers] if ssl_options.key?(:ciphers)
|
175
|
-
options[:protocols] = ssl_options.fetch(:alpn_protocols, %w[h2 http/1.1])
|
176
|
-
options[:hostname] = ssl_options.fetch(:hostname, @hostname)
|
177
|
-
options
|
178
|
-
end
|
179
|
-
|
180
|
-
def transition(nextstate)
|
181
|
-
case nextstate
|
182
|
-
when :negotiated
|
183
|
-
return unless @state == :connected
|
184
|
-
when :closed
|
185
|
-
return unless @state == :negotiated ||
|
186
|
-
@state == :connected
|
187
|
-
end
|
188
|
-
do_transition(nextstate)
|
189
|
-
end
|
190
|
-
|
191
|
-
def log_transition_state(nextstate)
|
192
|
-
return super unless nextstate == :negotiated
|
193
|
-
|
194
|
-
server_cert = @peer_cert
|
195
|
-
|
196
|
-
"#{super}\n\n" \
|
197
|
-
"SSL connection using #{@ctx.ssl_version} / #{Array(@ctx.cipher).first}\n" \
|
198
|
-
"ALPN, server accepted to use #{protocol}\n" +
|
199
|
-
(if server_cert
|
200
|
-
"Server certificate:\n" \
|
201
|
-
" subject: #{server_cert.subject}\n" \
|
202
|
-
" start date: #{server_cert.not_before}\n" \
|
203
|
-
" expire date: #{server_cert.not_after}\n" \
|
204
|
-
" issuer: #{server_cert.issuer}\n" \
|
205
|
-
" SSL certificate verify ok."
|
206
|
-
else
|
207
|
-
"SSL certificate verify failed."
|
208
|
-
end
|
209
|
-
)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
TLSError = TLS::Error
|
214
|
-
end
|
215
|
-
|
216
|
-
require "httpx/io/tls/ffi"
|
217
|
-
require "httpx/io/tls/context"
|
218
|
-
require "httpx/io/tls/box"
|