httpx 0.19.3 → 0.19.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,390 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "ffi"
4
- require "ffi-compiler/loader"
5
- require "concurrent"
6
-
7
- # Copyright (c) 2004-2013 Cotag Media
8
- #
9
- # Permission is hereby granted, free of charge, to any person obtaining a copy
10
- # of this software and associated documentation files (the "Software"), to deal
11
- # in the Software without restriction, including without limitation the rights
12
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
- # copies of the Software, and to permit persons to whom the Software is furnished
14
- # to do so, subject to the following conditions:
15
- #
16
- # The above copyright notice and this permission notice shall be included in all
17
- # copies or substantial portions of the Software.
18
- #
19
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
- # THE SOFTWARE.
26
- #
27
-
28
- module HTTPX::TLS::SSL
29
- Error = HTTPX::TLS::Error
30
-
31
- extend FFI::Library
32
-
33
- if FFI::Platform.windows?
34
- begin
35
- ffi_lib "libeay32", "ssleay32"
36
- rescue LoadError
37
- ffi_lib "libcrypto-1_1-x64", "libssl-1_1-x64"
38
- end
39
- else
40
- ffi_lib "ssl"
41
- end
42
-
43
- # Common structures
44
- typedef :pointer, :user_data
45
- typedef :pointer, :bio
46
- typedef :pointer, :evp_key
47
- typedef :pointer, :evp_key_pointer
48
- typedef :pointer, :x509
49
- typedef :pointer, :x509_pointer
50
- typedef :pointer, :ssl
51
- typedef :pointer, :cipher
52
- typedef :pointer, :ssl_ctx
53
- typedef :int, :buffer_length
54
- typedef :int, :pass_length
55
- typedef :int, :read_write_flag
56
-
57
- SSL_ST_OK = 0x03
58
- begin
59
- attach_function :SSL_library_init, [], :int
60
- attach_function :SSL_load_error_strings, [], :void
61
- attach_function :ERR_load_crypto_strings, [], :void
62
-
63
- attach_function :SSL_state, [:ssl], :int
64
- def self.is_init_finished(ssl)
65
- SSL_state(ssl) == SSL_ST_OK
66
- end
67
-
68
- OPENSSL_V1_1 = false
69
- rescue FFI::NotFoundError
70
- OPENSSL_V1_1 = true
71
- OPENSSL_INIT_LOAD_SSL_STRINGS = 0x200000
72
- OPENSSL_INIT_NO_LOAD_SSL_STRINGS = 0x100000
73
- attach_function :OPENSSL_init_ssl, %i[uint64 pointer], :int
74
-
75
- attach_function :SSL_get_state, [:ssl], :int
76
- attach_function :SSL_is_init_finished, [:ssl], :bool
77
-
78
- def self.is_init_finished(ssl)
79
- SSL_is_init_finished(ssl)
80
- end
81
- end
82
-
83
- # Multi-threaded support
84
- # callback :locking_cb, [:int, :int, :string, :int], :void
85
- # callback :thread_id_cb, [], :ulong
86
- # attach_function :CRYPTO_num_locks, [], :int
87
- # attach_function :CRYPTO_set_locking_callback, [:locking_cb], :void
88
- # attach_function :CRYPTO_set_id_callback, [:thread_id_cb], :void
89
-
90
- # InitializeDefaultCredentials
91
- attach_function :BIO_new_mem_buf, %i[string buffer_length], :bio
92
- attach_function :EVP_PKEY_free, [:evp_key], :void
93
-
94
- callback :pem_password_cb, %i[pointer buffer_length read_write_flag user_data], :pass_length
95
- attach_function :PEM_read_bio_PrivateKey, %i[bio evp_key_pointer pem_password_cb user_data], :evp_key
96
-
97
- attach_function :X509_free, [:x509], :void
98
- attach_function :PEM_read_bio_X509, %i[bio x509_pointer pem_password_cb user_data], :x509
99
-
100
- attach_function :BIO_free, [:bio], :int
101
-
102
- # GetPeerCert
103
- attach_function :SSL_get_peer_certificate, [:ssl], :x509
104
-
105
- # PutPlaintext
106
- attach_function :SSL_write, %i[ssl buffer_in buffer_length], :int
107
- attach_function :SSL_get_error, %i[ssl int], :int
108
-
109
- # GetCiphertext
110
- attach_function :BIO_read, %i[bio buffer_out buffer_length], :int
111
-
112
- # CanGetCiphertext
113
- attach_function :BIO_ctrl, %i[bio int long pointer], :long
114
- BIO_CTRL_PENDING = 10 # opt - is their more data buffered?
115
- def self.BIO_pending(bio)
116
- BIO_ctrl(bio, BIO_CTRL_PENDING, 0, nil)
117
- end
118
-
119
- # GetPlaintext
120
- attach_function :SSL_accept, [:ssl], :int
121
- attach_function :SSL_read, %i[ssl buffer_out buffer_length], :int
122
- attach_function :SSL_pending, [:ssl], :int
123
-
124
- # PutCiphertext
125
- attach_function :BIO_write, %i[bio buffer_in buffer_length], :int
126
-
127
- # Deconstructor
128
- attach_function :SSL_get_shutdown, [:ssl], :int
129
- attach_function :SSL_shutdown, [:ssl], :int
130
- attach_function :SSL_clear, [:ssl], :void
131
- attach_function :SSL_free, [:ssl], :void
132
-
133
- # Constructor
134
- attach_function :BIO_s_mem, [], :pointer
135
- attach_function :BIO_new, [:pointer], :bio
136
- attach_function :SSL_new, [:ssl_ctx], :ssl
137
- # r, w
138
- attach_function :SSL_set_bio, %i[ssl bio bio], :void
139
-
140
- attach_function :SSL_set_ex_data, %i[ssl int string], :int
141
- callback :verify_callback, %i[int x509], :int
142
- attach_function :SSL_set_verify, %i[ssl int verify_callback], :void
143
- attach_function :SSL_CTX_set_verify, %i[ssl int verify_callback], :void
144
- attach_function :SSL_get_verify_result, %i[ssl], :long
145
- attach_function :SSL_connect, [:ssl], :int
146
-
147
- # Verify callback
148
- X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = 2
149
- X509_V_ERR_HOSTNAME_MISMATCH = 62
150
- X509_V_ERR_CERT_REJECTED = 28
151
- attach_function :X509_STORE_CTX_get_current_cert, [:pointer], :x509
152
- attach_function :SSL_get_ex_data_X509_STORE_CTX_idx, [], :int
153
- attach_function :X509_STORE_CTX_get_ex_data, %i[pointer int], :ssl
154
- attach_function :X509_STORE_CTX_get_error_depth, %i[x509], :int
155
- attach_function :PEM_write_bio_X509, %i[bio x509], :bool
156
- attach_function :X509_verify_cert_error_string, %i[long], :string
157
- attach_function :X509_STORE_CTX_set_error, %i[ssl_ctx long], :void
158
-
159
- # SSL Context Class
160
- # OpenSSL before 1.1.0 do not have these methods
161
- # https://www.openssl.org/docs/man1.1.0/ssl/TLSv1_2_server_method.html
162
- begin
163
- attach_function :TLS_server_method, [], :pointer
164
- attach_function :TLS_client_method, [], :pointer
165
- rescue FFI::NotFoundError
166
- attach_function :SSLv23_server_method, [], :pointer
167
- attach_function :SSLv23_client_method, [], :pointer
168
-
169
- def self.TLS_server_method
170
- self.SSLv23_server_method
171
- end
172
-
173
- def self.TLS_client_method
174
- self.SSLv23_client_method
175
- end
176
- end
177
-
178
- # Version can be one of:
179
- # :SSL3, :TLS1, :TLS1_1, :TLS1_2, :TLS1_3, :TLS_MAX
180
- begin
181
- attach_function :SSL_get_version, %i[ssl], :string
182
- attach_function :SSL_get_current_cipher, %i[ssl], :cipher
183
- attach_function :SSL_CIPHER_get_name, %i[cipher], :string
184
- attach_function :SSL_CTX_set_min_proto_version, %i[ssl_ctx int], :int
185
- attach_function :SSL_CTX_set_max_proto_version, %i[ssl_ctx int], :int
186
-
187
- VERSION_SUPPORTED = true
188
-
189
- SSL3_VERSION = 0x0300
190
- TLS1_VERSION = 0x0301
191
- TLS1_1_VERSION = 0x0302
192
- TLS1_2_VERSION = 0x0303
193
- TLS1_3_VERSION = 0x0304
194
- TLS_MAX_VERSION = TLS1_3_VERSION
195
- ANY_VERSION = 0
196
- rescue FFI::NotFoundError
197
- VERSION_SUPPORTED = false
198
- end
199
-
200
- def self.get_version(ssl)
201
- SSL_get_version(ssl)
202
- end
203
-
204
- def self.get_current_cipher(ssl)
205
- cipher = SSL_get_current_cipher(ssl)
206
- SSL_CIPHER_get_name(cipher)
207
- end
208
-
209
- attach_function :SSL_CTX_new, [:pointer], :ssl_ctx
210
-
211
- attach_function :SSL_CTX_ctrl, %i[ssl_ctx int ulong pointer], :long
212
- SSL_CTRL_OPTIONS = 32
213
- def self.SSL_CTX_set_options(ssl_ctx, op)
214
- SSL_CTX_ctrl(ssl_ctx, SSL_CTRL_OPTIONS, op, nil)
215
- end
216
- SSL_CTRL_MODE = 33
217
- def self.SSL_CTX_set_mode(ssl_ctx, op)
218
- SSL_CTX_ctrl(ssl_ctx, SSL_CTRL_MODE, op, nil)
219
- end
220
- SSL_CTRL_SET_SESS_CACHE_SIZE = 42
221
- def self.SSL_CTX_sess_set_cache_size(ssl_ctx, op)
222
- SSL_CTX_ctrl(ssl_ctx, SSL_CTRL_SET_SESS_CACHE_SIZE, op, nil)
223
- end
224
-
225
- attach_function :SSL_ctrl, %i[ssl int long pointer], :long
226
- SSL_CTRL_SET_TLSEXT_HOSTNAME = 55
227
-
228
- def self.SSL_set_tlsext_host_name(ssl, host_name)
229
- name_ptr = FFI::MemoryPointer.from_string(host_name)
230
- raise Error, "error setting SNI hostname" if SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name_ptr).zero?
231
- end
232
-
233
- # Server Name Indication (SNI) Support
234
- # NOTE:: We've hard coded the callback here (SSL defines a NULL callback)
235
- callback :ssl_servername_cb, %i[ssl pointer pointer], :int
236
- attach_function :SSL_CTX_callback_ctrl, %i[ssl_ctx int ssl_servername_cb], :long
237
- SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53
238
- def self.SSL_CTX_set_tlsext_servername_callback(ctx, callback)
239
- SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, callback)
240
- end
241
-
242
- attach_function :SSL_get_servername, %i[ssl int], :string
243
- TLSEXT_NAMETYPE_host_name = 0
244
-
245
- attach_function :SSL_set_SSL_CTX, %i[ssl ssl_ctx], :ssl_ctx
246
-
247
- SSL_TLSEXT_ERR_OK = 0
248
- SSL_TLSEXT_ERR_ALERT_WARNING = 1
249
- SSL_TLSEXT_ERR_ALERT_FATAL = 2
250
- SSL_TLSEXT_ERR_NOACK = 3
251
-
252
- attach_function :SSL_CTX_use_PrivateKey_file, %i[ssl_ctx string int], :int, :blocking => true
253
- attach_function :SSL_CTX_use_PrivateKey, %i[ssl_ctx pointer], :int
254
- attach_function :ERR_print_errors_fp, [:pointer], :void # Pointer == File Handle
255
- attach_function :SSL_CTX_use_certificate_chain_file, %i[ssl_ctx string], :int, :blocking => true
256
- attach_function :SSL_CTX_use_certificate, %i[ssl_ctx x509], :int
257
- attach_function :SSL_CTX_set_cipher_list, %i[ssl_ctx string], :int
258
- attach_function :SSL_CTX_set_session_id_context, %i[ssl_ctx string buffer_length], :int
259
- attach_function :SSL_load_client_CA_file, [:string], :pointer
260
- attach_function :SSL_CTX_set_client_CA_list, %i[ssl_ctx pointer], :void
261
- attach_function :SSL_CTX_load_verify_locations, %i[ssl_ctx pointer], :int, :blocking => true
262
-
263
- # OpenSSL before 1.0.2 do not have these methods
264
- begin
265
- attach_function :SSL_CTX_set_alpn_protos, %i[ssl_ctx string uint], :int
266
-
267
- OPENSSL_NPN_UNSUPPORTED = 0
268
- OPENSSL_NPN_NEGOTIATED = 1
269
- OPENSSL_NPN_NO_OVERLAP = 2
270
-
271
- attach_function :SSL_select_next_proto, %i[pointer pointer string uint string uint], :int
272
-
273
- # array of str, unit8 out,uint8 in, *arg
274
- callback :alpn_select_cb, %i[ssl pointer pointer string uint pointer], :int
275
- attach_function :SSL_CTX_set_alpn_select_cb, %i[ssl_ctx alpn_select_cb pointer], :void
276
-
277
- attach_function :SSL_get0_alpn_selected, %i[ssl pointer pointer], :void
278
- ALPN_SUPPORTED = true
279
- rescue FFI::NotFoundError
280
- ALPN_SUPPORTED = false
281
- end
282
-
283
- # Deconstructor
284
- attach_function :SSL_CTX_free, [:ssl_ctx], :void
285
-
286
- PrivateMaterials = <<~KEYSTR
287
- -----BEGIN RSA PRIVATE KEY-----
288
- MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV
289
- Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/
290
- AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB
291
- AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk
292
- H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D
293
- I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo
294
- 6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg
295
- w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK
296
- PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ
297
- xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k
298
- xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa
299
- dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn
300
- 2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=
301
- -----END RSA PRIVATE KEY-----
302
- -----BEGIN CERTIFICATE-----
303
- MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD
304
- VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw
305
- FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG
306
- A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu
307
- ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw
308
- NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH
309
- EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n
310
- aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI
311
- hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB
312
- AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw
313
- VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3
314
- 9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID
315
- AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV
316
- HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG
317
- EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD
318
- VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE
319
- AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy
320
- aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG
321
- SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0
322
- Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j
323
- uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy
324
- -----END CERTIFICATE-----
325
- KEYSTR
326
-
327
- BuiltinPasswdCB = FFI::Function.new(:int, %i[pointer int int pointer]) do |buffer, _len, _flag, _data|
328
- buffer.write_string("kittycat")
329
- 8
330
- end
331
-
332
- # Save RAM by releasing read and write buffers when they're empty
333
- SSL_MODE_RELEASE_BUFFERS = 0x00000010
334
- SSL_OP_ALL = 0x80000BFF
335
- SSL_FILETYPE_PEM = 1
336
-
337
- # Locking isn't provided as long as all writes are done on the same thread.
338
- # This is my main use case. Happy to enable it if someone requires it and can
339
- # get it to work on MRI Ruby (Currently only works on JRuby and Rubinius)
340
- # as MRI callbacks occur on a thread pool?
341
-
342
- # CRYPTO_LOCK = 0x1
343
- # LockingCB = FFI::Function.new(:void, [:int, :int, :string, :int]) do |mode, type, file, line|
344
- # if (mode & CRYPTO_LOCK) != 0
345
- # SSL_LOCKS[type].lock
346
- # else
347
- # Unlock a lock
348
- # SSL_LOCKS[type].unlock
349
- # end
350
- # end
351
- # ThreadIdCB = FFI::Function.new(:ulong, []) do
352
- # Thread.current.object_id
353
- # end
354
-
355
- # INIT CODE
356
- @init_required ||= false
357
- unless @init_required
358
- if OPENSSL_V1_1
359
- self.OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, ::FFI::Pointer::NULL)
360
- else
361
- self.SSL_load_error_strings
362
- self.SSL_library_init
363
- self.ERR_load_crypto_strings
364
- end
365
-
366
- # Setup multi-threaded support
367
- # SSL_LOCKS = []
368
- # num_locks = self.CRYPTO_num_locks
369
- # num_locks.times { SSL_LOCKS << Mutex.new }
370
-
371
- # self.CRYPTO_set_locking_callback(LockingCB)
372
- # self.CRYPTO_set_id_callback(ThreadIdCB)
373
-
374
- bio = self.BIO_new_mem_buf(PrivateMaterials, PrivateMaterials.bytesize)
375
-
376
- # Get the private key structure
377
- pointer = FFI::MemoryPointer.new(:pointer)
378
- self.PEM_read_bio_PrivateKey(bio, pointer, BuiltinPasswdCB, nil)
379
- DEFAULT_PRIVATE = pointer.get_pointer(0)
380
-
381
- # Get the certificate structure
382
- pointer = FFI::MemoryPointer.new(:pointer)
383
- self.PEM_read_bio_X509(bio, pointer, nil, nil)
384
- DEFAULT_CERT = pointer.get_pointer(0)
385
-
386
- self.BIO_free(bio)
387
-
388
- @init_required = true
389
- end
390
- end
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"