| 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use Perlbal::Test; |
|---|
| 5 | use Perlbal::Test::WebServer; |
|---|
| 6 | use Perlbal::Test::WebClient; |
|---|
| 7 | use IO::Socket::INET; |
|---|
| 8 | use Test::More 'no_plan'; |
|---|
| 9 | |
|---|
| 10 | # setup webserver |
|---|
| 11 | my $web_port = start_webserver(); |
|---|
| 12 | ok($web_port, 'webserver started'); |
|---|
| 13 | |
|---|
| 14 | # setup perlbal |
|---|
| 15 | my $port = new_port(); |
|---|
| 16 | my $dir = tempdir(); |
|---|
| 17 | |
|---|
| 18 | my $conf = qq{ |
|---|
| 19 | SERVER aio_mode = none |
|---|
| 20 | |
|---|
| 21 | CREATE POOL a |
|---|
| 22 | POOL a ADD 127.0.0.1:$web_port |
|---|
| 23 | |
|---|
| 24 | CREATE SERVICE test |
|---|
| 25 | SET role = reverse_proxy |
|---|
| 26 | SET pool = a |
|---|
| 27 | SET connect_ahead = 0 |
|---|
| 28 | SET listen = 127.0.0.1:$port |
|---|
| 29 | SET persist_client = 1 |
|---|
| 30 | SET buffer_uploads_path = $dir |
|---|
| 31 | SET buffer_uploads = 1 |
|---|
| 32 | ENABLE test |
|---|
| 33 | }; |
|---|
| 34 | |
|---|
| 35 | my $msock = start_server($conf); |
|---|
| 36 | ok($msock, 'perlbal started'); |
|---|
| 37 | |
|---|
| 38 | ok(! buffer_file_exists(), "no files in buffer directory"); |
|---|
| 39 | |
|---|
| 40 | # setup data |
|---|
| 41 | my $data = 'x' x 1_000_000; |
|---|
| 42 | my ($curpos, $clen) = (0, 0); |
|---|
| 43 | |
|---|
| 44 | my $req; |
|---|
| 45 | |
|---|
| 46 | # disable all of it |
|---|
| 47 | request("buffer_off", 500_000, |
|---|
| 48 | "write:500", |
|---|
| 49 | "write:5", |
|---|
| 50 | "write:5", |
|---|
| 51 | "write:5", |
|---|
| 52 | "sleep:0.25", |
|---|
| 53 | "exists", |
|---|
| 54 | "write:100000", |
|---|
| 55 | "write:60000", |
|---|
| 56 | "write:1000", |
|---|
| 57 | "finish", |
|---|
| 58 | sub { |
|---|
| 59 | my ($res) = @_; |
|---|
| 60 | my $cont = $res->content; |
|---|
| 61 | like($cont, qr/length = 500000/, "backend got right content-length"); |
|---|
| 62 | }, |
|---|
| 63 | "empty"); |
|---|
| 64 | |
|---|
| 65 | sub buffer_file_exists { |
|---|
| 66 | opendir DIR, $dir |
|---|
| 67 | or die "can't open dir\n"; |
|---|
| 68 | foreach (readdir(DIR)) { |
|---|
| 69 | next if /^\./; |
|---|
| 70 | return 1; |
|---|
| 71 | } |
|---|
| 72 | return 0; |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | # cmds can be: |
|---|
| 76 | # write:<length> writes <length> bytes |
|---|
| 77 | # sleep:<duration> sleeps <duration> seconds, may be fractional |
|---|
| 78 | # finish (sends any final writes and/or reads response) |
|---|
| 79 | # close close socket |
|---|
| 80 | # sub {} coderef to run. gets passed response object |
|---|
| 81 | # no-reason response has no reason |
|---|
| 82 | # reason:<reason> did buffering for either "size", "rate", or "time" |
|---|
| 83 | # empty No files in temp buffer location |
|---|
| 84 | # exists Yes, a temporary file exists |
|---|
| 85 | sub request { |
|---|
| 86 | my $testname = shift; |
|---|
| 87 | my $len = shift || 0; |
|---|
| 88 | my @cmds = @_; |
|---|
| 89 | |
|---|
| 90 | my $curpos = 0; |
|---|
| 91 | my $remain = $len; |
|---|
| 92 | |
|---|
| 93 | my $hdr = "POST /status HTTP/1.0\r\nTransfer-Encoding: chunked\r\nExpect: 100-continue\r\n\r\n"; |
|---|
| 94 | my $sock = IO::Socket::INET->new( PeerAddr => "127.0.0.1:$port" ) |
|---|
| 95 | or return undef; |
|---|
| 96 | my $rv = syswrite($sock, $hdr); |
|---|
| 97 | die unless $rv == length($hdr); |
|---|
| 98 | |
|---|
| 99 | # wanting HTTP/1.1 100 Continue\r\n...\r\n lines |
|---|
| 100 | { |
|---|
| 101 | my $contline = <$sock>; |
|---|
| 102 | die "didn't get 100 Continue line, got: $contline" |
|---|
| 103 | unless $contline =~ m!^HTTP/1.1 100!; |
|---|
| 104 | my $gotempty = 0; |
|---|
| 105 | while (defined(my $line = <$sock>)) { |
|---|
| 106 | if ($line eq "\r\n") { |
|---|
| 107 | $gotempty = 1; |
|---|
| 108 | last; |
|---|
| 109 | } |
|---|
| 110 | } |
|---|
| 111 | die "didn't get empty line after 100 Continue" unless $gotempty; |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | my $res = undef; # no response yet |
|---|
| 115 | |
|---|
| 116 | foreach my $cmd (@cmds) { |
|---|
| 117 | my $writelen; |
|---|
| 118 | |
|---|
| 119 | if ($cmd =~ /^write:([\d_]+)/) { |
|---|
| 120 | $writelen = $1; |
|---|
| 121 | $writelen =~ s/_//g; |
|---|
| 122 | } elsif ($cmd =~ /^(\d+)/) { |
|---|
| 123 | $writelen = $1; |
|---|
| 124 | } elsif ($cmd eq "finish") { |
|---|
| 125 | $writelen = $remain; |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | if ($cmd =~ /^sleep:([\d\.]+)/) { |
|---|
| 129 | select undef, undef, undef, $1; |
|---|
| 130 | next; |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | if ($cmd eq "close") { |
|---|
| 134 | close($sock); |
|---|
| 135 | next; |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | if ($cmd eq "exists") { |
|---|
| 139 | ok(buffer_file_exists(), "$testname: buffer file exists"); |
|---|
| 140 | next; |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | if ($cmd eq "empty") { |
|---|
| 144 | ok(! buffer_file_exists(), "$testname: no file"); |
|---|
| 145 | next; |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | if ($writelen) { |
|---|
| 149 | die "Too long" if $writelen > $remain; |
|---|
| 150 | my $buf = "x" x $writelen; |
|---|
| 151 | $buf = sprintf("%x\r\n", $writelen) . $buf . "\r\n"; |
|---|
| 152 | $remain -= $writelen; |
|---|
| 153 | if ($remain == 0) { |
|---|
| 154 | # one \r\n for chunk ending, one for chunked-body ending, |
|---|
| 155 | # after (our empty) trailer... |
|---|
| 156 | $buf .= "0\r\n\r\n"; |
|---|
| 157 | } |
|---|
| 158 | my $bufsize = length($buf); |
|---|
| 159 | my $off = 0; |
|---|
| 160 | while ($off < $bufsize) { |
|---|
| 161 | my $rv = syswrite($sock, $buf, $bufsize-$off, $off); |
|---|
| 162 | die "Error writing: $!" unless defined $rv; |
|---|
| 163 | die "Got rv=0 from syswrite" unless $rv; |
|---|
| 164 | $off += $rv; |
|---|
| 165 | } |
|---|
| 166 | |
|---|
| 167 | next unless $cmd eq "finish"; |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | if ($cmd eq "finish") { |
|---|
| 171 | $res = resp_from_sock($sock); |
|---|
| 172 | my $clen = $res ? $res->header('Content-Length') : 0; |
|---|
| 173 | ok($res && length($res->content) == $clen, "$testname: good response"); |
|---|
| 174 | next; |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | if (ref $cmd eq "CODE") { |
|---|
| 178 | $cmd->($res, $testname); |
|---|
| 179 | next; |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | die "Invalid command: $cmd\n"; |
|---|
| 183 | } |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | 1; |
|---|