From 3a9df5c5951d18e94356f60a965a570e9e3a7021 Mon Sep 17 00:00:00 2001 From: MozerBYU Date: Fri, 20 Mar 2026 01:10:50 -0600 Subject: [PATCH 1/2] test(ssl): add ssl_rtt.t coverage for $ssl_handshake_rtt --- ssl_rtt.t | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 ssl_rtt.t diff --git a/ssl_rtt.t b/ssl_rtt.t new file mode 100644 index 00000000..da03e300 --- /dev/null +++ b/ssl_rtt.t @@ -0,0 +1,162 @@ +#!/usr/bin/perl + +# (C) MozerBYU + +# Tests for http ssl module, $ssl_handshake_rtt variable. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +use IPC::Open3; +use Symbol qw/ gensym /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +plan(skip_all => 'win32') if $^O eq 'MSWin32'; + +my $t = Test::Nginx->new() + ->has(qw/http http_ssl rewrite/) + ->has_daemon('openssl'); + +$t->write_file_expand('nginx.conf', <<'EOF_CONF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + log_format rtt '$ssl_protocol:$ssl_handshake_rtt'; + + server { + listen 127.0.0.1:8443 ssl; + server_name localhost; + + ssl_certificate_key localhost.key; + ssl_certificate localhost.crt; + ssl_protocols TLSv1.2 TLSv1.3; + + access_log %%TESTDIR%%/rtt.log rtt; + + location / { + return 200 "ok"; + } + } +} + +EOF_CONF + +$t->write_file('openssl.conf', <<'EOF_CERT'); +[ req ] +default_bits = 2048 +encrypt_key = no +distinguished_name = req_distinguished_name +[ req_distinguished_name ] +EOF_CERT + +my $d = $t->testdir(); + +system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=localhost/ " + . "-out $d/localhost.crt -keyout $d/localhost.key " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for localhost: $!\n"; + +$t->run()->plan(11); + +############################################################################### + +# use openssl s_client to force specific TLS protocol versions + +my ($out, $err, $rc); + +($out, $err, $rc) = openssl_get('-tls1'); +unlike($out, qr/200 OK/ms, 'TLSv1.0 rejected'); + +($out, $err, $rc) = openssl_get('-tls1_1'); +unlike($out, qr/200 OK/ms, 'TLSv1.1 rejected'); + +($out, $err, $rc) = openssl_get('-tls1_2'); +like($out, qr/200 OK/ms, 'TLSv1.2 request'); + +# OpenSSL 3.2+ exposes handshake RTT when it can be calculated + +my ($tls13_out, $tls13_err, $tls13_rc); + +if ($t->has_module('OpenSSL') && $t->has_feature('openssl:3.2')) { + ($tls13_out, $tls13_err, $tls13_rc) = openssl_get('-tls1_3'); +} + +$t->stop(); + +my @log = grep { length } split /\n/, eval { $t->read_file('rtt.log') }; +my @rejected = grep { $_ eq '-:-' } @log; +my ($tls12) = grep { /^TLSv1\.2:/ } @log; + +is(scalar(@log), defined $tls13_out ? 4 : 3, 'access log lines'); +is(scalar(@rejected), 2, 'pre-TLSv1.2 rejected logged'); +ok(defined $tls12, 'TLSv1.2 logged'); +like($tls12, qr/^TLSv1\.2:(?:\d+)?$/, 'TLSv1.2 handshake rtt'); + +SKIP: { + skip 'OpenSSL 3.2+ build', 1 + if $t->has_module('OpenSSL') && $t->has_feature('openssl:3.2'); + skip 'non-OpenSSL build', 1 + unless $t->has_module('OpenSSL'); + + is($tls12, 'TLSv1.2:', 'pre-3.2 handshake rtt empty'); +} + +SKIP: { + skip 'OpenSSL before 3.2', 3 + unless $t->has_module('OpenSSL') && $t->has_feature('openssl:3.2'); + + my ($tls13) = grep { /^TLSv1\.3:/ } @log; + + like($tls13_out, qr/200 OK/ms, 'TLSv1.3 request'); + ok(defined $tls13, 'TLSv1.3 logged'); + like($tls13, qr/^TLSv1\.3:\d+$/, 'TLSv1.3 handshake rtt'); +} + +############################################################################### + +sub openssl_get { + my ($flag) = @_; + my ($in, $out, $err); + + $err = gensym(); + + my $pid = open3($in, $out, $err, 'openssl', 's_client', + '-connect', '127.0.0.1:' . port(8443), + '-servername', 'localhost', + '-quiet', $flag); + + print $in "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n"; + close $in; + + local $/; + my $stdout = <$out>; + my $stderr = <$err>; + + waitpid($pid, 0); + + return ($stdout || '', $stderr || '', $? >> 8); +} + +############################################################################### From de56872093d64ae5a22a9a0b2fd81e9dc2e8176c Mon Sep 17 00:00:00 2001 From: MozerBYU Date: Wed, 25 Mar 2026 18:57:37 -0600 Subject: [PATCH 2/2] test(ssl): split ssl_rtt coverage for http and stream --- ssl_rtt.t => ssl_rtt_http.t | 5 +- ssl_rtt_stream.t | 136 ++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 4 deletions(-) rename ssl_rtt.t => ssl_rtt_http.t (95%) create mode 100644 ssl_rtt_stream.t diff --git a/ssl_rtt.t b/ssl_rtt_http.t similarity index 95% rename from ssl_rtt.t rename to ssl_rtt_http.t index da03e300..0880bc58 100644 --- a/ssl_rtt.t +++ b/ssl_rtt_http.t @@ -78,7 +78,7 @@ system('openssl req -x509 -new ' . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for localhost: $!\n"; -$t->run()->plan(11); +$t->run()->plan(9); ############################################################################### @@ -106,11 +106,8 @@ if ($t->has_module('OpenSSL') && $t->has_feature('openssl:3.2')) { $t->stop(); my @log = grep { length } split /\n/, eval { $t->read_file('rtt.log') }; -my @rejected = grep { $_ eq '-:-' } @log; my ($tls12) = grep { /^TLSv1\.2:/ } @log; -is(scalar(@log), defined $tls13_out ? 4 : 3, 'access log lines'); -is(scalar(@rejected), 2, 'pre-TLSv1.2 rejected logged'); ok(defined $tls12, 'TLSv1.2 logged'); like($tls12, qr/^TLSv1\.2:(?:\d+)?$/, 'TLSv1.2 handshake rtt'); diff --git a/ssl_rtt_stream.t b/ssl_rtt_stream.t new file mode 100644 index 00000000..36b5694a --- /dev/null +++ b/ssl_rtt_stream.t @@ -0,0 +1,136 @@ +#!/usr/bin/perl + +# (C) MozerBYU + +# Tests for stream ssl module, $ssl_handshake_rtt variable. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +use IPC::Open3; +use Symbol qw/ gensym /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +plan(skip_all => 'win32') if $^O eq 'MSWin32'; + +my $t = Test::Nginx->new() + ->has(qw/stream stream_ssl stream_return/) + ->has_daemon('openssl'); + +$t->write_file_expand('nginx.conf', <<'EOF_CONF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +stream { + %%TEST_GLOBALS_STREAM%% + + server { + listen 127.0.0.1:8443 ssl; + ssl_certificate_key localhost.key; + ssl_certificate localhost.crt; + ssl_protocols TLSv1.2 TLSv1.3; + + return "$ssl_protocol:$ssl_handshake_rtt"; + } +} + +EOF_CONF + +$t->write_file('openssl.conf', <<'EOF_CERT'); +[ req ] +default_bits = 2048 +encrypt_key = no +distinguished_name = req_distinguished_name +[ req_distinguished_name ] +EOF_CERT + +my $d = $t->testdir(); + +system('openssl req -x509 -new ' + . "-config $d/openssl.conf -subj /CN=localhost/ " + . "-out $d/localhost.crt -keyout $d/localhost.key " + . ">>$d/openssl.out 2>&1") == 0 + or die "Can't create certificate for localhost: $!\n"; + +$t->run()->plan(7); + +############################################################################### + +my ($out, $err, $rc); + +($out, $err, $rc) = openssl_get('-tls1'); +unlike($out, qr/^TLSv1\.0:/ms, 'TLSv1.0 rejected'); + +($out, $err, $rc) = openssl_get('-tls1_1'); +unlike($out, qr/^TLSv1\.1:/ms, 'TLSv1.1 rejected'); + +($out, $err, $rc) = openssl_get('-tls1_2'); +like($out, qr/^TLSv1\.2:(?:\d+)?$/ms, 'TLSv1.2 handshake rtt'); + +SKIP: { + skip 'OpenSSL 3.2+ build', 1 + if $t->has_module('OpenSSL') && $t->has_feature('openssl:3.2'); + skip 'non-OpenSSL build', 1 + unless $t->has_module('OpenSSL'); + + is($out, 'TLSv1.2:', 'pre-3.2 handshake rtt empty'); +} + +SKIP: { + skip 'OpenSSL before 3.2', 3 + unless $t->has_module('OpenSSL') && $t->has_feature('openssl:3.2'); + + my ($tls13_out, $tls13_err, $tls13_rc) = openssl_get('-tls1_3'); + + like($tls13_out, qr/^TLSv1\.3:\d+$/ms, 'TLSv1.3 handshake rtt'); + unlike($tls13_out, qr/^TLSv1\.3:$/ms, 'TLSv1.3 handshake rtt not empty'); + is($tls13_rc, 0, 'TLSv1.3 request'); +} + +$t->stop(); + +############################################################################### + +sub openssl_get { + my ($flag) = @_; + my ($in, $out, $err); + + $err = gensym(); + + my $pid = open3($in, $out, $err, 'openssl', 's_client', + '-connect', '127.0.0.1:' . port(8443), + '-servername', 'localhost', + '-quiet', $flag); + + close $in; + + local $/; + my $stdout = <$out>; + my $stderr = <$err>; + + waitpid($pid, 0); + + $stdout =~ s/[\r\n]+\z// if defined $stdout; + + return ($stdout || '', $stderr || '', $? >> 8); +} + +###############################################################################