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.
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"