[PATCH 10 of 11] Tests: reworked http SSL tests to use IO::Socket::SSL

Maxim Dounin mdounin at mdounin.ru
Mon Apr 17 03:31:34 UTC 2023


# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1681702264 -10800
#      Mon Apr 17 06:31:04 2023 +0300
# Node ID 2aaba5bbc0366bffe1f468105b1185cd48efbc93
# Parent  90913cb36b512c45cd9a171cbb4320b12ff24b48
Tests: reworked http SSL tests to use IO::Socket::SSL.

Relevant infrastructure is provided in Test::Nginx http() functions.
This also ensures that SSL handshake and various read and write operations
are guarded with timeouts.

The ssl_sni_reneg.t test uses IO::Socket::SSL::_get_ssl_object() to access
the Net::SSLeay object directly and trigger renegotation.  While
not exactly correct, this seems to be good enough for tests.

Similarly, IO::Socket::SSL::_get_ssl_object() is used in ssl_stapling.t,
since SSL_ocsp_staple_callback is called with the socket instead of the
Net::SSLeay object.

Similarly, IO::Socket::SSL::_get_ssl_object() is used in ssl_verify_client.t,
since there seems to be no way to obtain CA list with IO::Socket::SSL.

Notable change to http() request interface is that http_end() now closes
the socket.  This is to make sure that SSL connections are properly
closed and SSL sessions are not removed from the IO::Socket::SSL session
cache.  This affected access_log.t, which was modified accordingly.

diff --git a/access_log.t b/access_log.t
--- a/access_log.t
+++ b/access_log.t
@@ -161,11 +161,11 @@ http_get('/varlog?logname=0');
 http_get('/varlog?logname=filename');
 
 my $s = http('', start => 1);
-http_get('/addr', socket => $s);
 my $addr = $s->sockhost();
 my $port = $s->sockport();
 my $saddr = $s->peerhost();
 my $sport = $s->peerport();
+http_get('/addr', socket => $s);
 
 http_get('/binary');
 
diff --git a/lib/Test/Nginx.pm b/lib/Test/Nginx.pm
--- a/lib/Test/Nginx.pm
+++ b/lib/Test/Nginx.pm
@@ -838,13 +838,15 @@ sub http($;%) {
 	my $s = http_start($request, %extra);
 
 	return $s if $extra{start} or !defined $s;
-	return http_end($s);
+	return http_end($s, %extra);
 }
 
 sub http_start($;%) {
 	my ($request, %extra) = @_;
 	my $s;
 
+	my $port = $extra{SSL} ? 8443 : 8080;
+
 	eval {
 		local $SIG{ALRM} = sub { die "timeout\n" };
 		local $SIG{PIPE} = sub { die "sigpipe\n" };
@@ -852,10 +854,25 @@ sub http_start($;%) {
 
 		$s = $extra{socket} || IO::Socket::INET->new(
 			Proto => 'tcp',
-			PeerAddr => '127.0.0.1:' . port(8080)
+			PeerAddr => '127.0.0.1:' . port($port),
+			%extra
 		)
 			or die "Can't connect to nginx: $!\n";
 
+		if ($extra{SSL}) {
+			require IO::Socket::SSL;
+			IO::Socket::SSL->start_SSL(
+				$s,
+				SSL_verify_mode =>
+					IO::Socket::SSL::SSL_VERIFY_NONE(),
+				%extra
+			)
+				or die $IO::Socket::SSL::SSL_ERROR . "\n";
+
+			log_in("ssl cipher: " . $s->get_cipher());
+			log_in("ssl cert: " . $s->peer_certificate('issuer'));
+		}
+
 		log_out($request);
 		$s->print($request);
 
@@ -879,7 +896,7 @@ sub http_start($;%) {
 }
 
 sub http_end($;%) {
-	my ($s) = @_;
+	my ($s, %extra) = @_;
 	my $reply;
 
 	eval {
@@ -890,6 +907,8 @@ sub http_end($;%) {
 		local $/;
 		$reply = $s->getline();
 
+		$s->close();
+
 		alarm(0);
 	};
 	alarm(0);
diff --git a/ssl_certificate.t b/ssl_certificate.t
--- a/ssl_certificate.t
+++ b/ssl_certificate.t
@@ -17,29 +17,15 @@ use Socket qw/ CRLF /;
 BEGIN { use FindBin; chdir($FindBin::Bin); }
 
 use lib 'lib';
-use Test::Nginx;
+use Test::Nginx qw/ :DEFAULT http_end /;
 
 ###############################################################################
 
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-};
-plan(skip_all => 'Net::SSLeay not installed') if $@;
-
-eval {
-	my $ctx = Net::SSLeay::CTX_new() or die;
-	my $ssl = Net::SSLeay::new($ctx) or die;
-	Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die;
-};
-plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@;
-
-my $t = Test::Nginx->new()->has(qw/http http_ssl geo openssl:1.0.2/)
+my $t = Test::Nginx->new()
+	->has(qw/http http_ssl geo openssl:1.0.2 socket_ssl_sni/)
 	->has_daemon('openssl');
 
 $t->write_file_expand('nginx.conf', <<'EOF');
@@ -67,6 +53,7 @@ http {
     }
 
     add_header X-SSL $ssl_server_name:$ssl_session_reused;
+    add_header X-SSL-Protocol $ssl_protocol;
     ssl_session_cache shared:SSL:1m;
     ssl_session_tickets on;
 
@@ -177,60 +164,63 @@ like(get('password', 8083), qr/password/
 
 # session reuse
 
-my ($s, $ssl) = get('default', 8080);
-my $ses = Net::SSLeay::get_session($ssl);
-
-like(get('default', 8080, $ses), qr/default:r/, 'session reused');
+my $s = session('default', 8080);
 
 TODO: {
-# ticket key name mismatch prevents session resumption
+local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'
+	if $Net::SSLeay::VERSION < 1.88 && test_tls13();
+local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'
+	if $IO::Socket::SSL::VERSION < 2.061 && test_tls13();
+
+like(get('default', 8080, $s), qr/default:r/, 'session reused');
+
+TODO: {
+# automatic ticket ticket key name mismatch prevents session resumption
 local $TODO = 'not yet' unless $t->has_version('1.23.2');
 
-like(get('default', 8081, $ses), qr/default:r/, 'session id context match');
+like(get('default', 8081, $s), qr/default:r/, 'session id context match');
 
 }
+}
 
-like(get('default', 8082, $ses), qr/default:\./, 'session id context distinct');
+like(get('default', 8082, $s), qr/default:\./, 'session id context distinct');
 
 # errors
 
-Net::SSLeay::ERR_clear_error();
-get_ssl_socket('nx', 8084);
-ok(Net::SSLeay::ERR_peek_error(), 'no certificate');
+ok(!get('nx', 8084), 'no certificate');
 
 ###############################################################################
 
 sub get {
-	my ($host, $port, $ctx) = @_;
-	my ($s, $ssl) = get_ssl_socket($host, $port, $ctx) or return;
-
-	local $SIG{PIPE} = 'IGNORE';
-
-	Net::SSLeay::write($ssl, 'GET / HTTP/1.0' . CRLF . CRLF);
-	my $r = Net::SSLeay::read($ssl);
-	Net::SSLeay::shutdown($ssl);
-	$s->close();
-	return $r unless wantarray();
-	return ($s, $ssl);
+	my $s = get_socket(@_) || return;
+	return http_end($s);
 }
 
 sub cert {
-	my ($host, $port, $ctx) = @_;
-	my ($s, $ssl) = get_ssl_socket($host, $port, $ctx) or return;
-	Net::SSLeay::dump_peer_certificate($ssl);
+	my $s = get_socket(@_) || return;
+	return $s->dump_peer_certificate();
+}
+
+sub session {
+	my $s = get_socket(@_) || return;
+	http_end($s);
+	return $s;
 }
 
-sub get_ssl_socket {
-	my ($host, $port, $ses) = @_;
+sub get_socket {
+	my ($host, $port, $ctx) = @_;
+	return http_get(
+		'/', start => 1, PeerAddr => '127.0.0.1:' . port($port),
+		SSL => 1,
+		SSL_hostname => $host,
+		SSL_session_cache_size => 100,
+		SSL_session_key => 1,
+		SSL_reuse_ctx => $ctx
+	);
+}
 
-	my $s = IO::Socket::INET->new('127.0.0.1:' . port($port));
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_tlsext_host_name($ssl, $host);
-	Net::SSLeay::set_session($ssl, $ses) if defined $ses;
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl);
-	return ($s, $ssl);
+sub test_tls13 {
+	return get('default', 8080) =~ /TLSv1.3/;
 }
 
 ###############################################################################
diff --git a/ssl_certificate_perl.t b/ssl_certificate_perl.t
--- a/ssl_certificate_perl.t
+++ b/ssl_certificate_perl.t
@@ -22,23 +22,8 @@ use Test::Nginx;
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-};
-plan(skip_all => 'Net::SSLeay not installed') if $@;
-
-eval {
-	my $ctx = Net::SSLeay::CTX_new() or die;
-	my $ssl = Net::SSLeay::new($ctx) or die;
-	Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die;
-};
-plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@;
-
 my $t = Test::Nginx->new()
-	->has(qw/http http_ssl perl openssl:1.0.2/)
+	->has(qw/http http_ssl perl openssl:1.0.2 socket_ssl_sni/)
 	->has_daemon('openssl');
 
 $t->write_file_expand('nginx.conf', <<'EOF');
@@ -66,7 +51,7 @@ http {
     ';
 
     server {
-        listen       127.0.0.1:8080 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  localhost;
 
         ssl_certificate data:$pem;
@@ -98,27 +83,19 @@ foreach my $name ('one', 'two') {
 
 ###############################################################################
 
-like(cert('one', 8080), qr/CN=one/, 'certificate');
-like(cert('two', 8080), qr/CN=two/, 'certificate 2');
+like(cert('one'), qr/CN=one/, 'certificate');
+like(cert('two'), qr/CN=two/, 'certificate 2');
 
 ###############################################################################
 
 sub cert {
-	my ($host, $port) = @_;
-	my ($s, $ssl) = get_ssl_socket($host, $port) or return;
-	Net::SSLeay::dump_peer_certificate($ssl);
+	my $s = get_socket(@_) || return;
+	return $s->dump_peer_certificate();
 }
 
-sub get_ssl_socket {
-	my ($host, $port) = @_;
-
-	my $s = IO::Socket::INET->new('127.0.0.1:' . port($port));
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_tlsext_host_name($ssl, $host);
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-	return ($s, $ssl);
+sub get_socket {
+	my $host = shift;
+	return http_get('/', start => 1, SSL => 1, SSL_hostname => $host);
 }
 
 ###############################################################################
diff --git a/ssl_certificates.t b/ssl_certificates.t
--- a/ssl_certificates.t
+++ b/ssl_certificates.t
@@ -22,16 +22,8 @@ use Test::Nginx;
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-	Net::SSLeay::SSLeay();
-};
-plan(skip_all => 'Net::SSLeay not installed or too old') if $@;
-
-my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl');
+my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)
+	->has_daemon('openssl');
 
 plan(skip_all => 'no multiple certificates') if $t->has_module('BoringSSL');
 
@@ -51,8 +43,10 @@ http {
     ssl_certificate rsa.crt;
     ssl_ciphers DEFAULT:ECCdraft;
 
+    add_header X-SSL-Protocol $ssl_protocol;
+
     server {
-        listen       127.0.0.1:8080 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  localhost;
 
         ssl_certificate_key ec.key;
@@ -91,65 +85,54 @@ foreach my $name ('ec', 'rsa') {
 		or die "Can't create certificate for $name: $!\n";
 }
 
+$t->write_file('index.html', '');
+
 $t->run()->plan(2);
 
 ###############################################################################
 
-like(get_cert('RSA'), qr/CN=rsa/, 'ssl cert RSA');
-like(get_cert('ECDSA'), qr/CN=ec/, 'ssl cert ECDSA');
+TODO: {
+local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'
+	if $t->has_module('LibreSSL') && test_tls13();
+
+like(cert('RSA'), qr/CN=rsa/, 'ssl cert RSA');
+
+}
+
+like(cert('ECDSA'), qr/CN=ec/, 'ssl cert ECDSA');
 
 ###############################################################################
 
-sub get_version {
-	my ($s, $ssl) = get_ssl_socket();
-	return Net::SSLeay::version($ssl);
+sub test_tls13 {
+	return http_get('/', SSL => 1) =~ /TLSv1.3/;
 }
 
-sub get_cert {
-	my ($type) = @_;
-	$type = 'PSS' if $type eq 'RSA' && get_version() > 0x0303;
-	my ($s, $ssl) = get_ssl_socket($type);
-	my $cipher = Net::SSLeay::get_cipher($ssl);
-	Test::Nginx::log_core('||', "cipher: $cipher");
-	return Net::SSLeay::dump_peer_certificate($ssl);
+sub cert {
+	my $s = get_socket(@_) || return;
+	return $s->dump_peer_certificate();
 }
 
-sub get_ssl_socket {
+sub get_socket {
 	my ($type) = @_;
-	my $s;
-
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(8);
-		$s = IO::Socket::INET->new('127.0.0.1:' . port(8080));
-		alarm(0);
-	};
-	alarm(0);
-
-	if ($@) {
-		log_in("died: $@");
-		return undef;
-	}
 
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-
-	if (defined $type) {
+	my $ctx_cb = sub {
+		my $ctx = shift;
+		return unless defined $type;
 		my $ssleay = Net::SSLeay::SSLeay();
-		if ($ssleay < 0x1000200f || $ssleay == 0x20000000) {
-			Net::SSLeay::CTX_set_cipher_list($ctx, $type)
-				or die("Failed to set cipher list");
-		} else {
-			# SSL_CTRL_SET_SIGALGS_LIST
-			Net::SSLeay::CTX_ctrl($ctx, 98, 0, $type . '+SHA256')
-				or die("Failed to set sigalgs");
-		}
-	}
+		return if ($ssleay < 0x1000200f || $ssleay == 0x20000000);
+		my $sigalgs = 'RSA+SHA256:PSS+SHA256';
+		$sigalgs = $type . '+SHA256' unless $type eq 'RSA';
+		# SSL_CTRL_SET_SIGALGS_LIST
+		Net::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs)
+			or die("Failed to set sigalgs");
+	};
 
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-	return ($s, $ssl);
+	return http_get(
+		'/', start => 1,
+		SSL => 1,
+		SSL_cipher_list => $type,
+		SSL_create_ctx_callback => $ctx_cb
+	);
 }
 
 ###############################################################################
diff --git a/ssl_conf_command.t b/ssl_conf_command.t
--- a/ssl_conf_command.t
+++ b/ssl_conf_command.t
@@ -15,22 +15,15 @@ use Test::More;
 BEGIN { use FindBin; chdir($FindBin::Bin); }
 
 use lib 'lib';
-use Test::Nginx;
+use Test::Nginx qw/ :DEFAULT http_end /;
 
 ###############################################################################
 
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-};
-plan(skip_all => 'Net::SSLeay not installed') if $@;
-
-my $t = Test::Nginx->new()->has(qw/http http_ssl openssl:1.0.2/)
+my $t = Test::Nginx->new()
+	->has(qw/http http_ssl openssl:1.0.2 socket_ssl_reused/)
 	->has_daemon('openssl');
 
 plan(skip_all => 'no ssl_conf_command') if $t->has_module('BoringSSL');
@@ -91,32 +84,32 @@ foreach my $name ('localhost', 'override
 
 ###############################################################################
 
-my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
+my $s;
 
-my ($s, $ssl) = get_ssl_socket();
-like(Net::SSLeay::dump_peer_certificate($ssl), qr/CN=override/, 'Certificate');
+$s = http_get(
+	'/', start => 1,
+	SSL => 1,
+	SSL_session_cache_size => 100
+);
+
+like($s->dump_peer_certificate(), qr/CN=override/, 'Certificate');
+http_end($s);
 
-my $ses = Net::SSLeay::get_session($ssl);
-($s, $ssl) = get_ssl_socket(ses => $ses);
-ok(Net::SSLeay::session_reused($ssl), 'SessionTicket');
+$s = http_get(
+	'/', start => 1,
+	SSL => 1,
+	SSL_reuse_ctx => $s
+);
+
+ok($s->get_session_reused(), 'SessionTicket');
 
-($s, $ssl) = get_ssl_socket(ciphers =>
-	'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384');
-is(Net::SSLeay::get_cipher($ssl),
-	'ECDHE-RSA-AES128-GCM-SHA256', 'ServerPreference');
+$s = http_get(
+	'/', start => 1,
+	SSL => 1,
+	SSL_cipher_list =>
+		'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'
+);
+
+is($s->get_cipher(), 'ECDHE-RSA-AES128-GCM-SHA256', 'ServerPreference');
 
 ###############################################################################
-
-sub get_ssl_socket {
-	my (%extra) = @_;
-
-	my $s = IO::Socket::INET->new('127.0.0.1:' . port(8443));
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_session($ssl, $extra{ses}) if $extra{ses};
-	Net::SSLeay::set_cipher_list($ssl, $extra{ciphers}) if $extra{ciphers};
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-	return ($s, $ssl);
-}
-
-###############################################################################
diff --git a/ssl_ocsp.t b/ssl_ocsp.t
--- a/ssl_ocsp.t
+++ b/ssl_ocsp.t
@@ -17,31 +17,15 @@ use MIME::Base64 qw/ decode_base64 /;
 BEGIN { use FindBin; chdir($FindBin::Bin); }
 
 use lib 'lib';
-use Test::Nginx;
+use Test::Nginx qw/ :DEFAULT http_end /;
 
 ###############################################################################
 
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-	Net::SSLeay::SSLeay();
-	defined &Net::SSLeay::set_tlsext_status_type or die;
-};
-plan(skip_all => 'Net::SSLeay not installed or too old') if $@;
-
-eval {
-	my $ctx = Net::SSLeay::CTX_new() or die;
-	my $ssl = Net::SSLeay::new($ctx) or die;
-	Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die;
-};
-plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@;
-
-my $t = Test::Nginx->new()->has(qw/http http_ssl sni/)->has_daemon('openssl');
+my $t = Test::Nginx->new()->has(qw/http http_ssl sni socket_ssl_sni/)
+	->has_daemon('openssl');
 
 plan(skip_all => 'no OCSP support in BoringSSL')
 	if $t->has_module('BoringSSL');
@@ -70,6 +54,7 @@ http {
     ssl_session_tickets off;
 
     add_header X-Verify x${ssl_client_verify}:${ssl_session_reused}x always;
+    add_header X-SSL-Protocol $ssl_protocol always;
 
     server {
         listen       127.0.0.1:8443 ssl;
@@ -283,8 +268,6 @@ foreach my $name ('rsa') {
 $t->waitforsocket("127.0.0.1:" . port(8081));
 $t->waitforsocket("127.0.0.1:" . port(8082));
 
-my $version = get_version();
-
 ###############################################################################
 
 like(get('end'), qr/200 OK.*SUCCESS/s, 'ocsp leaf');
@@ -366,14 +349,17 @@ system("openssl ocsp -index $d/certindex
 
 like(get('ec-end'), qr/200 OK.*SUCCESS/s, 'ocsp ecdsa');
 
-my ($s, $ssl) = get('ec-end');
-my $ses = Net::SSLeay::get_session($ssl);
+my $s = session('ec-end');
 
 TODO: {
+local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'
+	if $Net::SSLeay::VERSION < 1.88 && test_tls13();
+local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'
+	if $IO::Socket::SSL::VERSION < 2.061 && test_tls13();
 local $TODO = 'no TLSv1.3 sessions in LibreSSL'
-	if $t->has_module('LibreSSL') and $version > 0x303;
+	if $t->has_module('LibreSSL') && test_tls13();
 
-like(get('ec-end', ses => $ses),
+like(get('ec-end', ses => $s),
 	qr/200 OK.*SUCCESS:r/s, 'session reused');
 
 }
@@ -398,10 +384,14 @@ system("openssl ocsp -index $d/certindex
 # reusing session with revoked certificate
 
 TODO: {
+local $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'
+	if $Net::SSLeay::VERSION < 1.88 && test_tls13();
+local $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'
+	if $IO::Socket::SSL::VERSION < 2.061 && test_tls13();
 local $TODO = 'no TLSv1.3 sessions in LibreSSL'
-	if $t->has_module('LibreSSL') and $version > 0x303;
+	if $t->has_module('LibreSSL') && test_tls13();
 
-like(get('ec-end', ses => $ses),
+like(get('ec-end', ses => $s),
 	qr/400 Bad.*FAILED:certificate revoked:r/s, 'session reused - revoked');
 
 }
@@ -417,57 +407,38 @@ like(`grep -F '[crit]' ${\($t->testdir()
 ###############################################################################
 
 sub get {
-	my ($cert, %extra) = @_;
-	my ($s, $ssl) = get_ssl_socket($cert, %extra);
-	my $cipher = Net::SSLeay::get_cipher($ssl);
-	Test::Nginx::log_core('||', "cipher: $cipher");
-	my $host = $extra{sni} ? $extra{sni} : 'localhost';
-	local $SIG{PIPE} = 'IGNORE';
-	log_out("GET /serial HTTP/1.0\nHost: $host\n\n");
-	Net::SSLeay::write($ssl, "GET /serial HTTP/1.0\nHost: $host\n\n");
-	my $r = Net::SSLeay::read($ssl);
-	log_in($r);
-	$s->close();
-	return $r unless wantarray();
-	return ($s, $ssl);
+	my $s = get_socket(@_) || return;
+	return http_end($s);
 }
 
-sub get_ssl_socket {
+sub session {
+	my $s = get_socket(@_) || return;
+	http_end($s);
+	return $s;
+}
+
+sub get_socket {
 	my ($cert, %extra) = @_;
 	my $ses = $extra{ses};
-	my $sni = $extra{sni};
+	my $sni = $extra{sni} || 'localhost';
 	my $port = $extra{port} || 8443;
-	my $s;
-
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(8);
-		$s = IO::Socket::INET->new('127.0.0.1:' . port($port));
-		alarm(0);
-	};
-	alarm(0);
 
-	if ($@) {
-		log_in("died: $@");
-		return undef;
-	}
-
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-
-	Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key")
-		or die if $cert;
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_session($ssl, $ses) if defined $ses;
-	Net::SSLeay::set_tlsext_host_name($ssl, $sni) if $sni;
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-	return ($s, $ssl);
+	return http(
+		"GET /serial HTTP/1.0\nHost: $sni\n\n",
+		start => 1, PeerAddr => '127.0.0.1:' . port($port),
+		SSL => 1,
+		SSL_hostname => $sni,
+		SSL_session_cache_size => 100,
+		SSL_reuse_ctx => $ses,
+		$cert ? (
+		SSL_cert_file => "$d/$cert.crt",
+		SSL_key_file => "$d/$cert.key"
+		) : ()
+	);
 }
 
-sub get_version {
-	my ($s, $ssl) = get_ssl_socket();
-	return Net::SSLeay::version($ssl);
+sub test_tls13 {
+	return http_get('/', SSL => 1) =~ /TLSv1.3/;
 }
 
 ###############################################################################
diff --git a/ssl_session_ticket_key.t b/ssl_session_ticket_key.t
--- a/ssl_session_ticket_key.t
+++ b/ssl_session_ticket_key.t
@@ -15,23 +15,19 @@ use Test::More;
 BEGIN { use FindBin; chdir($FindBin::Bin); }
 
 use lib 'lib';
-use Test::Nginx;
+use Test::Nginx qw/ :DEFAULT http_end /;
 
 ###############################################################################
 
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay; die if $Net::SSLeay::VERSION < 1.86;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-};
+eval { require Net::SSLeay; die if $Net::SSLeay::VERSION < 1.86; };
 plan(skip_all => 'Net::SSLeay version => 1.86 required') if $@;
 
-my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl')
-	->plan(2)->write_file_expand('nginx.conf', <<'EOF');
+my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)
+	->has_daemon('openssl')->plan(2)
+	->write_file_expand('nginx.conf', <<'EOF');
 
 %%TEST_GLOBALS%%
 
@@ -47,8 +43,10 @@ http {
     ssl_certificate_key localhost.key;
     ssl_certificate localhost.crt;
 
+    add_header X-SSL-Protocol $ssl_protocol;
+
     server {
-        listen       127.0.0.1:8080 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  localhost;
 
         ssl_session_cache shared:SSL:1m;
@@ -76,6 +74,8 @@ foreach my $name ('localhost') {
 		or die "Can't create certificate for $name: $!\n";
 }
 
+$t->write_file('index.html', '');
+
 $t->run();
 
 ###############################################################################
@@ -105,8 +105,7 @@ cmp_ok(get_ticket_key_name(), 'ne', $key
 ###############################################################################
 
 sub get_ticket_key_name {
-	my $ses = get_ssl_session();
-	my $asn = Net::SSLeay::i2d_SSL_SESSION($ses);
+	my $asn = get_ssl_session();
 	my $any = qr/[\x00-\xff]/;
 next:
 	# tag(10) | len{2} | OCTETSTRING(4) | len{2} | ticket(key_name|..)
@@ -119,29 +118,25 @@ next:
 }
 
 sub get_ssl_session {
-	my ($s, $ssl) = get_ssl_socket();
+	my $cache = IO::Socket::SSL::Session_Cache->new(100);
 
-	Net::SSLeay::write($ssl, <<EOF);
-GET / HTTP/1.0
-Host: localhost
+	my $s = http_get(
+		'/', start => 1,
+		SSL => 1,
+		SSL_session_cache => $cache,
+		SSL_session_key => 1
+	);
 
-EOF
-	Net::SSLeay::read($ssl);
-	Net::SSLeay::get_session($ssl);
+	return unless $s;
+	http_end($s);
+
+	my $sess = $cache->get_session(1);
+	return '' unless defined $sess;
+	return Net::SSLeay::i2d_SSL_SESSION($sess);
 }
 
 sub test_tls13 {
-	my ($s, $ssl) = get_ssl_socket();
-	return (Net::SSLeay::version($ssl) > 0x303);
-}
-
-sub get_ssl_socket {
-	my $s = IO::Socket::INET->new('127.0.0.1:' . port(8080));
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-	return ($s, $ssl);
+	return http_get('/', SSL => 1) =~ /TLSv1.3/;
 }
 
 ###############################################################################
diff --git a/ssl_sni_reneg.t b/ssl_sni_reneg.t
--- a/ssl_sni_reneg.t
+++ b/ssl_sni_reneg.t
@@ -17,29 +17,15 @@ use Socket qw/ CRLF /;
 BEGIN { use FindBin; chdir($FindBin::Bin); }
 
 use lib 'lib';
-use Test::Nginx;
+use Test::Nginx qw/ :DEFAULT http_end /;
 
 ###############################################################################
 
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-};
-plan(skip_all => 'Net::SSLeay not installed') if $@;
-
-eval {
-	my $ctx = Net::SSLeay::CTX_new() or die;
-	my $ssl = Net::SSLeay::new($ctx) or die;
-	Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die;
-};
-plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@;
-
-my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl');
+my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl_sni/)
+	->has_daemon('openssl')->plan(8);
 
 $t->write_file_expand('nginx.conf', <<'EOF');
 
@@ -58,15 +44,15 @@ http {
     ssl_protocols TLSv1.2;
 
     server {
-        listen       127.0.0.1:8080 ssl;
-        listen       127.0.0.1:8081 ssl;
+        listen       127.0.0.1:8443 ssl;
+        listen       127.0.0.1:8444 ssl;
         server_name  localhost;
 
         location / { }
     }
 
     server {
-        listen       127.0.0.1:8081 ssl;
+        listen       127.0.0.1:8444 ssl;
         server_name  localhost2;
 
         location / { }
@@ -94,11 +80,12 @@ foreach my $name ('localhost') {
 }
 
 $t->run();
-$t->plan(8);
 
 ###############################################################################
 
-my ($s, $ssl) = get_ssl_socket(8080);
+my ($s, $ssl);
+
+$s = http('', start => 1, SSL => 1);
 ok($s, 'connection');
 
 SKIP: {
@@ -106,20 +93,26 @@ skip 'connection failed', 3 unless $s;
 
 local $SIG{PIPE} = 'IGNORE';
 
-Net::SSLeay::write($ssl, 'GET / HTTP/1.0' . CRLF);
+$s->print('GET / HTTP/1.0' . CRLF);
 
+# Note: this uses IO::Socket::SSL::_get_ssl_object() internal method.
+# While not exactly correct, it looks like there is no other way to
+# trigger renegotiation with IO::Socket::SSL, and this seems to be
+# good enough for tests.
+
+$ssl = $s->_get_ssl_object();
 ok(Net::SSLeay::renegotiate($ssl), 'renegotiation');
 ok(Net::SSLeay::set_tlsext_host_name($ssl, 'localhost'), 'SNI');
 
-Net::SSLeay::write($ssl, 'Host: localhost' . CRLF . CRLF);
+$s->print('Host: localhost' . CRLF . CRLF);
 
-ok(!Net::SSLeay::read($ssl), 'response');
+ok(!http_end($s), 'response');
 
 }
 
 # virtual servers
 
-($s, $ssl) = get_ssl_socket(8081);
+$s = http('', start => 1, PeerAddr => '127.0.0.1:' . port(8444), SSL => 1);
 ok($s, 'connection 2');
 
 SKIP: {
@@ -127,44 +120,21 @@ skip 'connection failed', 3 unless $s;
 
 local $SIG{PIPE} = 'IGNORE';
 
-Net::SSLeay::write($ssl, 'GET / HTTP/1.0' . CRLF);
+$s->print('GET / HTTP/1.0' . CRLF);
 
+# Note: this uses IO::Socket::SSL::_get_ssl_object() internal method.
+# While not exactly correct, it looks like there is no other way to
+# trigger renegotiation with IO::Socket::SSL, and this seems to be
+# good enough for tests.
+
+$ssl = $s->_get_ssl_object();
 ok(Net::SSLeay::renegotiate($ssl), 'renegotiation');
 ok(Net::SSLeay::set_tlsext_host_name($ssl, 'localhost'), 'SNI');
 
-Net::SSLeay::write($ssl, 'Host: localhost' . CRLF . CRLF);
+$s->print('Host: localhost' . CRLF . CRLF);
 
-ok(!Net::SSLeay::read($ssl), 'virtual servers');
+ok(!http_end($s), 'virtual servers');
 
 }
 
 ###############################################################################
-
-sub get_ssl_socket {
-	my ($port) = @_;
-	my $s;
-
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(8);
-		$s = IO::Socket::INET->new('127.0.0.1:' . port($port));
-		alarm(0);
-	};
-	alarm(0);
-
-	if ($@) {
-		log_in("died: $@");
-		return undef;
-	}
-
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::set_tlsext_host_name($ssl, 'localhost');
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-
-	return ($s, $ssl);
-}
-
-###############################################################################
diff --git a/ssl_stapling.t b/ssl_stapling.t
--- a/ssl_stapling.t
+++ b/ssl_stapling.t
@@ -24,17 +24,13 @@ use Test::Nginx;
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-	Net::SSLeay::SSLeay();
-	defined &Net::SSLeay::set_tlsext_status_type or die;
-};
-plan(skip_all => 'Net::SSLeay not installed or too old') if $@;
+my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)
+	->has_daemon('openssl');
 
-my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl');
+eval { defined &Net::SSLeay::set_tlsext_status_type or die; };
+plan(skip_all => 'Net::SSLeay too old') if $@;
+eval { defined &IO::Socket::SSL::SSL_OCSP_TRY_STAPLE or die; };
+plan(skip_all => 'IO::Socket::SSL too old') if $@;
 
 plan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL');
 
@@ -246,8 +242,6 @@ system("openssl ocsp -index $d/certindex
 
 ###############################################################################
 
-my $version = get_version();
-
 staple(8443, 'RSA');
 staple(8443, 'ECDSA');
 staple(8444, 'RSA');
@@ -262,7 +256,7 @@ ok(!staple(8443, 'RSA'), 'staple revoked
 
 TODO: {
 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'
-	if $t->has_module('LibreSSL') && $version > 0x303;
+	if $t->has_module('LibreSSL') && test_tls13();
 
 ok(staple(8443, 'ECDSA'), 'staple success');
 
@@ -272,7 +266,7 @@ ok(!staple(8444, 'RSA'), 'responder revo
 
 TODO: {
 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'
-	if $t->has_module('LibreSSL') && $version > 0x303;
+	if $t->has_module('LibreSSL') && test_tls13();
 
 ok(staple(8444, 'ECDSA'), 'responder success');
 
@@ -289,7 +283,7 @@ ok(!staple(8449, 'ECDSA'), 'ocsp error')
 
 TODO: {
 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'
-	if $t->has_module('LibreSSL') && $version > 0x303;
+	if $t->has_module('LibreSSL') && test_tls13();
 
 like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit');
 
@@ -302,9 +296,16 @@ sub staple {
 	my (@resp);
 
 	my $staple_cb = sub {
-		my ($ssl, $resp) = @_;
+		my ($s, $resp) = @_;
 		push @resp, !!$resp;
 		return 1 unless $resp;
+
+		# Contrary to the documentation, IO::Socket::SSL calls the
+		# SSL_ocsp_staple_callback with the socket, and not the
+		# Net::SSLeay object.
+
+		my $ssl = $s->_get_ssl_object();
+
 		my $cert = Net::SSLeay::get_peer_certificate($ssl);
 		my $certid = eval { Net::SSLeay::OCSP_cert2ids($ssl, $cert) }
 			or do { die "no OCSP_CERTID for certificate: $@"; };
@@ -313,69 +314,34 @@ sub staple {
 		push @resp, $res[0][2]->{'statusType'};
 	};
 
-	my $s;
-
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(8);
-		$s = IO::Socket::INET->new('127.0.0.1:' . port($port));
-		alarm(0);
+	my $ctx_cb = sub {
+		my $ctx = shift;
+		return unless defined $ciphers;
+		my $ssleay = Net::SSLeay::SSLeay();
+		return if ($ssleay < 0x1000200f || $ssleay == 0x20000000);
+		my $sigalgs = 'RSA+SHA256:PSS+SHA256';
+		$sigalgs = $ciphers . '+SHA256' unless $ciphers eq 'RSA';
+		# SSL_CTRL_SET_SIGALGS_LIST
+		Net::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs)
+			or die("Failed to set sigalgs");
 	};
-	alarm(0);
-
-	if ($@) {
-		log_in("died: $@");
-		return undef;
-	}
-
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
 
-	my $ssleay = Net::SSLeay::SSLeay();
-	if ($ssleay < 0x1000200f || $ssleay == 0x20000000) {
-		Net::SSLeay::CTX_set_cipher_list($ctx, $ciphers)
-			or die("Failed to set cipher list");
-	} else {
-		# SSL_CTRL_SET_SIGALGS_LIST
-		$ciphers = 'PSS' if $ciphers eq 'RSA' && $version > 0x0303;
-		Net::SSLeay::CTX_ctrl($ctx, 98, 0, $ciphers . '+SHA256')
-			or die("Failed to set sigalgs");
-	}
+	my $s = http_get(
+		'/', start => 1, PeerAddr => '127.0.0.1:' . port($port),
+		SSL => 1,
+		SSL_cipher_list => $ciphers,
+		SSL_create_ctx_callback => $ctx_cb,
+		SSL_ocsp_staple_callback => $staple_cb,
+		SSL_ocsp_mode => IO::Socket::SSL::SSL_OCSP_TRY_STAPLE(),
+		SSL_ca_file => $ca
+	);
 
-	Net::SSLeay::CTX_load_verify_locations($ctx, $ca || '', '');
-	Net::SSLeay::CTX_set_tlsext_status_cb($ctx, $staple_cb);
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_tlsext_status_type($ssl,
-		Net::SSLeay::TLSEXT_STATUSTYPE_ocsp());
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-
+	return $s unless $s;
 	return join ' ', @resp;
 }
 
-sub get_version {
-	my $s;
-
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(8);
-		$s = IO::Socket::INET->new('127.0.0.1:' . port(8443));
-		alarm(0);
-	};
-	alarm(0);
-
-	if ($@) {
-		log_in("died: $@");
-		return undef;
-	}
-
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
-
-	Net::SSLeay::version($ssl);
+sub test_tls13 {
+	return http_get('/', start => 1, SSL => 1) =~ /TLSv1.3/;
 }
 
 ###############################################################################
diff --git a/ssl_verify_client.t b/ssl_verify_client.t
--- a/ssl_verify_client.t
+++ b/ssl_verify_client.t
@@ -17,29 +17,14 @@ use Socket qw/ CRLF /;
 BEGIN { use FindBin; chdir($FindBin::Bin); }
 
 use lib 'lib';
-use Test::Nginx;
+use Test::Nginx qw/ :DEFAULT http_end /;
 
 ###############################################################################
 
 select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
-eval {
-	require Net::SSLeay;
-	Net::SSLeay::load_error_strings();
-	Net::SSLeay::SSLeay_add_ssl_algorithms();
-	Net::SSLeay::randomize();
-};
-plan(skip_all => 'Net::SSLeay not installed') if $@;
-
-eval {
-	my $ctx = Net::SSLeay::CTX_new() or die;
-	my $ssl = Net::SSLeay::new($ctx) or die;
-	Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die;
-};
-plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@;
-
-my $t = Test::Nginx->new()->has(qw/http http_ssl sni/)
+my $t = Test::Nginx->new()->has(qw/http http_ssl sni socket_ssl_sni/)
 	->has_daemon('openssl')->plan(13);
 
 $t->write_file_expand('nginx.conf', <<'EOF');
@@ -72,7 +57,7 @@ http {
     }
 
     server {
-        listen       127.0.0.1:8081 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  on;
 
         ssl_certificate_key 1.example.com.key;
@@ -83,7 +68,7 @@ http {
     }
 
     server {
-        listen       127.0.0.1:8081 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  optional;
 
         ssl_certificate_key 1.example.com.key;
@@ -95,7 +80,7 @@ http {
     }
 
     server {
-        listen       127.0.0.1:8081 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  off;
 
         ssl_certificate_key 1.example.com.key;
@@ -107,7 +92,7 @@ http {
     }
 
     server {
-        listen       127.0.0.1:8081 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  optional.no.ca;
 
         ssl_certificate_key 1.example.com.key;
@@ -118,7 +103,7 @@ http {
     }
 
     server {
-        listen       127.0.0.1:8081 ssl;
+        listen       127.0.0.1:8443 ssl;
         server_name  no.context;
 
         ssl_verify_client on;
@@ -191,25 +176,28 @@ sub test_tls13 {
 sub get {
 	my ($sni, $cert, $host) = @_;
 
-	local $SIG{PIPE} = 'IGNORE';
-
 	$host = $sni if !defined $host;
 
-	my $s = IO::Socket::INET->new('127.0.0.1:' . port(8081));
-	my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!");
-	Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key")
-		or die if $cert;
-	my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
-	Net::SSLeay::set_tlsext_host_name($ssl, $sni) == 1 or die;
-	Net::SSLeay::set_fd($ssl, fileno($s));
-	Net::SSLeay::connect($ssl) or die("ssl connect");
+	my $s = http(
+		"GET /t HTTP/1.0" . CRLF .
+		"Host: $host" . CRLF . CRLF,
+		start => 1,
+		SSL => 1,
+		SSL_hostname => $sni,
+		$cert ? (
+		SSL_cert_file => "$d/$cert.crt",
+		SSL_key_file => "$d/$cert.key"
+		) : ()
+	);
 
-	Net::SSLeay::write($ssl, 'GET /t HTTP/1.0' . CRLF);
-	Net::SSLeay::write($ssl, "Host: $host" . CRLF . CRLF);
-	my $buf = Net::SSLeay::read($ssl);
-	log_in($buf);
-	return $buf unless wantarray();
+	return http_end($s) unless wantarray();
 
+	# Note: this uses IO::Socket::SSL::_get_ssl_object() internal method.
+	# While not exactly correct, it looks like there is no other way to
+	# obtain CA list with IO::Socket::SSL, and this seems to be good
+	# enough for tests.
+
+	my $ssl = $s->_get_ssl_object();
 	my $list = Net::SSLeay::get_client_CA_list($ssl);
 	my @names;
 	for my $i (0 .. Net::SSLeay::sk_X509_NAME_num($list) - 1) {


More information about the nginx-devel mailing list