Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 1 | #!/usr/bin/perl |
| 2 | |
| 3 | # (C) Sergey Kandaurov |
| 4 | # (C) Nginx, Inc. |
| 5 | |
| 6 | # Stream tests for upstream module and balancers. |
| 7 | |
| 8 | ############################################################################### |
| 9 | |
| 10 | use warnings; |
| 11 | use strict; |
| 12 | |
| 13 | use Test::More; |
| 14 | |
| 15 | use IO::Select; |
| 16 | |
| 17 | BEGIN { use FindBin; chdir($FindBin::Bin); } |
| 18 | |
| 19 | use lib 'lib'; |
| 20 | use Test::Nginx; |
Andrey Zelenkov | 3f40149 | 2015-07-20 15:06:09 +0300 | [diff] [blame] | 21 | use Test::Nginx::Stream qw/ stream /; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 22 | |
| 23 | ############################################################################### |
| 24 | |
| 25 | select STDERR; $| = 1; |
| 26 | select STDOUT; $| = 1; |
| 27 | |
Sergey Kandaurov | 7be33d2 | 2016-09-05 22:57:44 +0300 | [diff] [blame] | 28 | my $t = Test::Nginx->new()->has(qw/stream/) |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 29 | ->write_file_expand('nginx.conf', <<'EOF'); |
| 30 | |
| 31 | %%TEST_GLOBALS%% |
| 32 | |
| 33 | daemon off; |
| 34 | |
| 35 | events { |
| 36 | } |
| 37 | |
| 38 | stream { |
Sergey Kandaurov | 7be33d2 | 2016-09-05 22:57:44 +0300 | [diff] [blame] | 39 | log_format bytes $upstream_addr! |
| 40 | $upstream_bytes_sent!$upstream_bytes_received; |
| 41 | |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 42 | upstream u { |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 43 | server 127.0.0.1:8084; |
| 44 | server 127.0.0.1:8085; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | upstream u2 { |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 48 | server 127.0.0.1:8086 down; |
| 49 | server 127.0.0.1:8086; |
| 50 | server 127.0.0.1:8084; |
| 51 | server 127.0.0.1:8085; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | upstream u3 { |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 55 | server 127.0.0.1:8084; |
| 56 | server 127.0.0.1:8085 weight=2; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | upstream u4 { |
Sergey Kandaurov | 82f138e | 2016-12-29 19:30:29 +0300 | [diff] [blame] | 60 | server 127.0.0.1:8086 fail_timeout=1s; |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 61 | server 127.0.0.1:8084 backup; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 62 | } |
| 63 | |
Sergey Kandaurov | cd95270 | 2019-09-09 15:42:31 +0300 | [diff] [blame] | 64 | proxy_connect_timeout 2; |
| 65 | |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 66 | server { |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 67 | listen 127.0.0.1:8080; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 68 | proxy_pass u; |
| 69 | } |
| 70 | |
| 71 | server { |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 72 | listen 127.0.0.1:8081; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 73 | proxy_pass u2; |
| 74 | } |
| 75 | |
| 76 | server { |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 77 | listen 127.0.0.1:8082; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 78 | proxy_pass u3; |
| 79 | } |
| 80 | |
| 81 | server { |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 82 | listen 127.0.0.1:8083; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 83 | proxy_pass u4; |
Sergey Kandaurov | 7be33d2 | 2016-09-05 22:57:44 +0300 | [diff] [blame] | 84 | access_log %%TESTDIR%%/u.log bytes; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 85 | } |
| 86 | } |
| 87 | |
| 88 | EOF |
| 89 | |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 90 | $t->run_daemon(\&stream_daemon, port(8084)); |
| 91 | $t->run_daemon(\&stream_daemon, port(8085)); |
Sergey Kandaurov | 9473f14 | 2017-11-24 19:58:40 +0300 | [diff] [blame] | 92 | $t->run()->plan(6); |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 93 | |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 94 | $t->waitforsocket('127.0.0.1:' . port(8084)); |
| 95 | $t->waitforsocket('127.0.0.1:' . port(8085)); |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 96 | |
| 97 | ############################################################################### |
| 98 | |
Sergey Kandaurov | 7be33d2 | 2016-09-05 22:57:44 +0300 | [diff] [blame] | 99 | my @ports = my ($port4, $port5, $port6) = (port(8084), port(8085), port(8086)); |
Andrey Zelenkov | c91d8f8 | 2016-06-21 16:39:13 +0300 | [diff] [blame] | 100 | |
Andrey Zelenkov | e59bf36 | 2016-07-12 17:39:03 +0300 | [diff] [blame] | 101 | is(many(30, port(8080)), "$port4: 15, $port5: 15", 'balanced'); |
| 102 | is(many(30, port(8081)), "$port4: 15, $port5: 15", 'failures'); |
| 103 | is(many(30, port(8082)), "$port4: 10, $port5: 20", 'weight'); |
| 104 | is(many(30, port(8083)), "$port4: 30", 'backup'); |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 105 | |
Sergey Kandaurov | 82f138e | 2016-12-29 19:30:29 +0300 | [diff] [blame] | 106 | $t->run_daemon(\&stream_daemon, port(8086)); |
| 107 | $t->waitforsocket('127.0.0.1:' . port(8086)); |
| 108 | |
Sergey Kandaurov | 82f138e | 2016-12-29 19:30:29 +0300 | [diff] [blame] | 109 | sleep 2; # wait till fail_timeout passes |
| 110 | is(parallel(30, port(8083)), "$port6: 30", 'recovery'); |
| 111 | |
Sergey Kandaurov | 7be33d2 | 2016-09-05 22:57:44 +0300 | [diff] [blame] | 112 | $t->stop(); |
| 113 | |
| 114 | like($t->read_file('u.log'), qr/127.0.0.1:$port6, 127.0.0.1:$port4!0, 1!0, 4/, |
| 115 | 'per-upstream variables'); |
| 116 | |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 117 | ############################################################################### |
| 118 | |
| 119 | sub many { |
Andrey Zelenkov | c91d8f8 | 2016-06-21 16:39:13 +0300 | [diff] [blame] | 120 | my ($count, $port) = @_; |
| 121 | my (%ports); |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 122 | |
| 123 | for (1 .. $count) { |
Andrey Zelenkov | c91d8f8 | 2016-06-21 16:39:13 +0300 | [diff] [blame] | 124 | if (stream("127.0.0.1:$port")->io('.') =~ /(\d+)/) { |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 125 | $ports{$1} = 0 unless defined $ports{$1}; |
| 126 | $ports{$1}++; |
| 127 | } |
| 128 | } |
| 129 | |
Andrey Zelenkov | c91d8f8 | 2016-06-21 16:39:13 +0300 | [diff] [blame] | 130 | my @keys = map { my $p = $_; grep { $p == $_ } keys %ports } @ports; |
| 131 | return join ', ', map { $_ . ": " . $ports{$_} } @keys; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 132 | } |
| 133 | |
Sergey Kandaurov | 82f138e | 2016-12-29 19:30:29 +0300 | [diff] [blame] | 134 | sub parallel { |
| 135 | my ($count, $port) = @_; |
| 136 | my (%ports, @s); |
| 137 | |
| 138 | for (1 .. $count) { |
| 139 | my $s = stream("127.0.0.1:$port"); |
| 140 | $s->write('keep'); |
| 141 | $s->read(); |
| 142 | push @s, $s; |
| 143 | } |
| 144 | |
| 145 | for (1 .. $count) { |
| 146 | if ((pop @s)->io('.') =~ /(\d+)/) { |
| 147 | $ports{$1} = 0 unless defined $ports{$1}; |
| 148 | $ports{$1}++; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | my @keys = map { my $p = $_; grep { $p == $_ } keys %ports } @ports; |
| 153 | return join ', ', map { $_ . ": " . $ports{$_} } @keys; |
| 154 | } |
| 155 | |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 156 | ############################################################################### |
| 157 | |
| 158 | sub stream_daemon { |
| 159 | my ($port) = @_; |
| 160 | |
| 161 | my $server = IO::Socket::INET->new( |
| 162 | Proto => 'tcp', |
| 163 | LocalAddr => '127.0.0.1', |
| 164 | LocalPort => $port, |
| 165 | Listen => 5, |
| 166 | Reuse => 1 |
| 167 | ) |
| 168 | or die "Can't create listening socket: $!\n"; |
| 169 | |
| 170 | my $sel = IO::Select->new($server); |
| 171 | |
| 172 | local $SIG{PIPE} = 'IGNORE'; |
| 173 | |
| 174 | while (my @ready = $sel->can_read) { |
| 175 | foreach my $fh (@ready) { |
| 176 | if ($server == $fh) { |
| 177 | my $new = $fh->accept; |
| 178 | $new->autoflush(1); |
| 179 | $sel->add($new); |
| 180 | |
| 181 | } elsif (stream_handle_client($fh)) { |
| 182 | $sel->remove($fh); |
| 183 | $fh->close; |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | sub stream_handle_client { |
| 190 | my ($client) = @_; |
| 191 | |
| 192 | log2c("(new connection $client)"); |
| 193 | |
| 194 | $client->sysread(my $buffer, 65536) or return 1; |
| 195 | |
| 196 | log2i("$client $buffer"); |
| 197 | |
Sergey Kandaurov | 82f138e | 2016-12-29 19:30:29 +0300 | [diff] [blame] | 198 | my $close = $buffer ne 'keep'; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 199 | $buffer = $client->sockport(); |
| 200 | |
| 201 | log2o("$client $buffer"); |
| 202 | |
| 203 | $client->syswrite($buffer); |
| 204 | |
Sergey Kandaurov | 82f138e | 2016-12-29 19:30:29 +0300 | [diff] [blame] | 205 | return $close; |
Sergey Kandaurov | 8402806 | 2015-04-22 16:59:40 +0300 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | sub log2i { Test::Nginx::log_core('|| <<', @_); } |
| 209 | sub log2o { Test::Nginx::log_core('|| >>', @_); } |
| 210 | sub log2c { Test::Nginx::log_core('||', @_); } |
| 211 | |
| 212 | ############################################################################### |