1*508def2aSbluhm# $OpenBSD: funcs.pl,v 1.26 2024/06/14 15:12:57 bluhm Exp $ 2c2d4e910Sbluhm 3b65dd5eaSbluhm# Copyright (c) 2010-2021 Alexander Bluhm <bluhm@openbsd.org> 4c2d4e910Sbluhm# 5c2d4e910Sbluhm# Permission to use, copy, modify, and distribute this software for any 6c2d4e910Sbluhm# purpose with or without fee is hereby granted, provided that the above 7c2d4e910Sbluhm# copyright notice and this permission notice appear in all copies. 8c2d4e910Sbluhm# 9c2d4e910Sbluhm# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10c2d4e910Sbluhm# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11c2d4e910Sbluhm# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12c2d4e910Sbluhm# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13c2d4e910Sbluhm# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14c2d4e910Sbluhm# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15c2d4e910Sbluhm# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16c2d4e910Sbluhm 17c2d4e910Sbluhmuse strict; 18c2d4e910Sbluhmuse warnings; 19c2d4e910Sbluhmuse Errno; 20c2d4e910Sbluhmuse Digest::MD5; 21c2d4e910Sbluhmuse Socket; 22c2d4e910Sbluhmuse Socket6; 23c2d4e910Sbluhmuse IO::Socket; 24b65dd5eaSbluhmuse IO::Socket::IP; 25c2d4e910Sbluhm 26c2d4e910Sbluhmsub find_ports { 27c2d4e910Sbluhm my %args = @_; 28c2d4e910Sbluhm my $num = delete $args{num} // 1; 29c2d4e910Sbluhm my $domain = delete $args{domain} // AF_INET; 30c2d4e910Sbluhm my $addr = delete $args{addr} // "127.0.0.1"; 31c2d4e910Sbluhm 32c2d4e910Sbluhm my @sockets = (1..$num); 33c2d4e910Sbluhm foreach my $s (@sockets) { 34b65dd5eaSbluhm $s = IO::Socket::IP->new( 35c2d4e910Sbluhm Proto => "tcp", 36b65dd5eaSbluhm Family => $domain, 37c2d4e910Sbluhm $addr ? (LocalAddr => $addr) : (), 38c2d4e910Sbluhm ) or die "find_ports: create and bind socket failed: $!"; 39c2d4e910Sbluhm } 40c2d4e910Sbluhm my @ports = map { $_->sockport() } @sockets; 41c2d4e910Sbluhm 42c2d4e910Sbluhm return @ports; 43c2d4e910Sbluhm} 44c2d4e910Sbluhm 45c2d4e910Sbluhm######################################################################## 46c2d4e910Sbluhm# Client funcs 47c2d4e910Sbluhm######################################################################## 48c2d4e910Sbluhm 493daec375Sbennosub write_syswrite { 503daec375Sbenno my $self = shift; 513daec375Sbenno my $buf = shift; 523daec375Sbenno 53c6fb90fcSbluhm IO::Handle::flush(\*STDOUT); 54c6fb90fcSbluhm my $size = length($buf); 55c6fb90fcSbluhm my $len = 0; 563daec375Sbenno while ($len < $size) { 57c6fb90fcSbluhm my $n = syswrite(STDOUT, $buf, $size, $len); 58c6fb90fcSbluhm if (!defined($n)) { 59c6fb90fcSbluhm $!{EWOULDBLOCK} 60c6fb90fcSbluhm or die ref($self), " syswrite failed: $!"; 61c6fb90fcSbluhm print STDERR "blocked write at $len of $size: $!\n"; 62c6fb90fcSbluhm next; 633daec375Sbenno } 64c6fb90fcSbluhm if ($len + $n != $size) { 65c6fb90fcSbluhm print STDERR "short write $n at $len of $size\n"; 663daec375Sbenno } 67c6fb90fcSbluhm $len += $n; 683daec375Sbenno } 693daec375Sbenno return $len; 703daec375Sbenno} 713daec375Sbenno 72c6fb90fcSbluhmsub write_block { 733daec375Sbenno my $self = shift; 743daec375Sbenno my $len = shift; 753daec375Sbenno 763daec375Sbenno my $data; 773daec375Sbenno my $outb = 0; 783daec375Sbenno my $blocks = int($len / 1000); 793daec375Sbenno my $rest = $len % 1000; 803daec375Sbenno 813daec375Sbenno for (my $i = 1; $i <= 100 ; $i++) { 82c6fb90fcSbluhm $data .= "012345678\n"; 833daec375Sbenno } 843daec375Sbenno 85c6fb90fcSbluhm my $opct = 0; 863daec375Sbenno for (my $i = 1; $i <= $blocks; $i++) { 873daec375Sbenno $outb += write_syswrite($self, $data); 88c6fb90fcSbluhm my $pct = ($outb / $len) * 100.0; 89c6fb90fcSbluhm if ($pct >= $opct + 1) { 90c6fb90fcSbluhm printf(STDERR "%.2f%% $outb/$len\n", $pct); 91c6fb90fcSbluhm $opct = $pct; 92c6fb90fcSbluhm } 933daec375Sbenno } 943daec375Sbenno 953daec375Sbenno if ($rest>0) { 963daec375Sbenno for (my $i = 1; $i < $rest-1 ; $i++) { 973daec375Sbenno $outb += write_syswrite($self, 'r'); 98c6fb90fcSbluhm my $pct = ($outb / $len) * 100.0; 99c6fb90fcSbluhm if ($pct >= $opct + 1) { 100c6fb90fcSbluhm printf(STDERR "%.2f%% $outb/$len\n", $pct); 101c6fb90fcSbluhm $opct = $pct; 1023daec375Sbenno } 1033daec375Sbenno } 104c6fb90fcSbluhm } 1053daec375Sbenno $outb += write_syswrite($self, "\n\n"); 1063daec375Sbenno IO::Handle::flush(\*STDOUT); 1073daec375Sbenno print STDERR "LEN: ", $outb, "\n"; 1083daec375Sbenno} 1093daec375Sbenno 110c2d4e910Sbluhmsub write_char { 111c2d4e910Sbluhm my $self = shift; 112c2d4e910Sbluhm my $len = shift // $self->{len} // 251; 113c2d4e910Sbluhm my $sleep = $self->{sleep}; 114c2d4e910Sbluhm 1153daec375Sbenno if ($self->{fast}) { 116c6fb90fcSbluhm write_block($self, $len); 1173daec375Sbenno return; 1183daec375Sbenno } 1193daec375Sbenno 120c2d4e910Sbluhm my $ctx = Digest::MD5->new(); 121c2d4e910Sbluhm my $char = '0'; 122c2d4e910Sbluhm for (my $i = 1; $i < $len; $i++) { 123c2d4e910Sbluhm $ctx->add($char); 124c2d4e910Sbluhm print $char 125c2d4e910Sbluhm or die ref($self), " print failed: $!"; 126*508def2aSbluhm if ($char =~ /9/) { $char = 'A' } 127*508def2aSbluhm elsif ($char =~ /Z/) { $char = 'a' } 128*508def2aSbluhm elsif ($char =~ /z/) { $char = "\n" } 129*508def2aSbluhm elsif ($char =~ /\n/) { print STDERR "."; $char = '0' } 130*508def2aSbluhm else { $char++ } 131c2d4e910Sbluhm if ($self->{sleep}) { 132c2d4e910Sbluhm IO::Handle::flush(\*STDOUT); 133c2d4e910Sbluhm sleep $self->{sleep}; 134c2d4e910Sbluhm } 135c2d4e910Sbluhm } 136c2d4e910Sbluhm if ($len) { 137c2d4e910Sbluhm $char = "\n"; 138c2d4e910Sbluhm $ctx->add($char); 139c2d4e910Sbluhm print $char 140c2d4e910Sbluhm or die ref($self), " print failed: $!"; 141c2d4e910Sbluhm print STDERR ".\n"; 142c2d4e910Sbluhm } 143c2d4e910Sbluhm IO::Handle::flush(\*STDOUT); 144c2d4e910Sbluhm 145c2d4e910Sbluhm print STDERR "LEN: ", $len, "\n"; 146c2d4e910Sbluhm print STDERR "MD5: ", $ctx->hexdigest, "\n"; 147c2d4e910Sbluhm} 148c2d4e910Sbluhm 1499d1e6fe8Sbluhmsub http_client { 1509d1e6fe8Sbluhm my $self = shift; 151cab60319Sbluhm 152cab60319Sbluhm unless ($self->{lengths}) { 153cab60319Sbluhm # only a single http request 154cab60319Sbluhm my $len = shift // $self->{len} // 251; 155cab60319Sbluhm my $cookie = $self->{cookie}; 156cab60319Sbluhm http_request($self, $len, "1.0", $cookie); 1571b3c3ba0Sbluhm http_response($self, $len); 158cab60319Sbluhm return; 159cab60319Sbluhm } 160cab60319Sbluhm 161cab60319Sbluhm $self->{http_vers} ||= ["1.1", "1.0"]; 162cab60319Sbluhm my $vers = $self->{http_vers}[0]; 163cab60319Sbluhm my @lengths = @{$self->{redo}{lengths} || $self->{lengths}}; 164cab60319Sbluhm my @cookies = @{$self->{redo}{cookies} || $self->{cookies} || []}; 165cab60319Sbluhm while (defined (my $len = shift @lengths)) { 166cab60319Sbluhm my $cookie = shift @cookies || $self->{cookie}; 1671b3c3ba0Sbluhm eval { 1681b3c3ba0Sbluhm http_request($self, $len, $vers, $cookie); 1691b3c3ba0Sbluhm http_response($self, $len); 1701b3c3ba0Sbluhm }; 171cab60319Sbluhm warn $@ if $@; 172cab60319Sbluhm if (@lengths && ($@ || $vers eq "1.0")) { 173cab60319Sbluhm # reconnect and redo the outstanding requests 174cab60319Sbluhm $self->{redo} = { 175cab60319Sbluhm lengths => \@lengths, 176cab60319Sbluhm cookies => \@cookies, 177cab60319Sbluhm }; 178cab60319Sbluhm return; 179cab60319Sbluhm } 180cab60319Sbluhm } 181cab60319Sbluhm delete $self->{redo}; 182cab60319Sbluhm shift @{$self->{http_vers}}; 183cab60319Sbluhm if (@{$self->{http_vers}}) { 184cab60319Sbluhm # run the tests again with other persistence 185cab60319Sbluhm $self->{redo} = { 186cab60319Sbluhm lengths => [@{$self->{lengths}}], 187cab60319Sbluhm cookies => [@{$self->{cookies} || []}], 188cab60319Sbluhm }; 189cab60319Sbluhm } 190cab60319Sbluhm} 191cab60319Sbluhm 192cab60319Sbluhmsub http_request { 193cab60319Sbluhm my ($self, $len, $vers, $cookie) = @_; 1943a4f532cSbluhm my $method = $self->{method} || "GET"; 195a3c410c1Sbluhm my %header = %{$self->{header} || {}}; 1969d1e6fe8Sbluhm 197c64e9fedSbluhm # encode the requested length or chunks into the url 1987582d965Sbluhm my $path = ref($len) eq 'ARRAY' ? join("/", @$len) : $len; 19985781182Sandre # overwrite path with custom path 20085781182Sandre if (defined($self->{path})) { 20185781182Sandre $path = $self->{path}; 20285781182Sandre } 203a3c410c1Sbluhm my @request = ("$method /$path HTTP/$vers"); 204a3c410c1Sbluhm push @request, "Host: foo.bar" unless defined $header{Host}; 2051b3c3ba0Sbluhm if ($vers eq "1.1" && $method eq "PUT") { 2061b3c3ba0Sbluhm if (ref($len) eq 'ARRAY') { 2071b3c3ba0Sbluhm push @request, "Transfer-Encoding: chunked" 2081b3c3ba0Sbluhm if !defined $header{'Transfer-Encoding'}; 2091b3c3ba0Sbluhm } else { 210c64e9fedSbluhm push @request, "Content-Length: $len" 2111b3c3ba0Sbluhm if !defined $header{'Content-Length'}; 2121b3c3ba0Sbluhm } 2131b3c3ba0Sbluhm } 2147e1bbd8eSreyk foreach my $key (sort keys %header) { 2157e1bbd8eSreyk my $val = $header{$key}; 2167e1bbd8eSreyk if (ref($val) eq 'ARRAY') { 2177e1bbd8eSreyk push @request, "$key: $_" 2187e1bbd8eSreyk foreach @{$val}; 2197e1bbd8eSreyk } else { 2207e1bbd8eSreyk push @request, "$key: $val"; 2217e1bbd8eSreyk } 2227e1bbd8eSreyk } 223cab60319Sbluhm push @request, "Cookie: $cookie" if $cookie; 224c64e9fedSbluhm push @request, ""; 225c64e9fedSbluhm print STDERR map { ">>> $_\n" } @request; 226c64e9fedSbluhm print map { "$_\r\n" } @request; 2271b3c3ba0Sbluhm if ($method eq "PUT") { 2281b3c3ba0Sbluhm if (ref($len) eq 'ARRAY') { 2291b3c3ba0Sbluhm if ($vers eq "1.1") { 2301b3c3ba0Sbluhm write_chunked($self, @$len); 2311b3c3ba0Sbluhm } else { 2321b3c3ba0Sbluhm write_char($self, $_) foreach (@$len); 2331b3c3ba0Sbluhm } 2341b3c3ba0Sbluhm } else { 2351b3c3ba0Sbluhm write_char($self, $len); 2361b3c3ba0Sbluhm } 2371b3c3ba0Sbluhm } 2389d1e6fe8Sbluhm IO::Handle::flush(\*STDOUT); 239a3c410c1Sbluhm # XXX client shutdown seems to be broken in relayd 240a3c410c1Sbluhm #shutdown(\*STDOUT, SHUT_WR) 241a3c410c1Sbluhm # or die ref($self), " shutdown write failed: $!" 242a3c410c1Sbluhm # if $vers ne "1.1"; 2431b3c3ba0Sbluhm} 2449d1e6fe8Sbluhm 2451b3c3ba0Sbluhmsub http_response { 2461b3c3ba0Sbluhm my ($self, $len) = @_; 2471b3c3ba0Sbluhm my $method = $self->{method} || "GET"; 2481b3c3ba0Sbluhm 2491b3c3ba0Sbluhm my $vers; 2507582d965Sbluhm my $chunked = 0; 2519d1e6fe8Sbluhm { 2529d1e6fe8Sbluhm local $/ = "\r\n"; 2539d1e6fe8Sbluhm local $_ = <STDIN>; 254a3c410c1Sbluhm defined 255cab60319Sbluhm or die ref($self), " missing http $len response"; 256cab60319Sbluhm chomp; 257cab60319Sbluhm print STDERR "<<< $_\n"; 2581b3c3ba0Sbluhm m{^HTTP/(\d\.\d) 200 OK$} 259cab60319Sbluhm or die ref($self), " http response not ok" 260cab60319Sbluhm unless $self->{httpnok}; 2611b3c3ba0Sbluhm $vers = $1; 2629d1e6fe8Sbluhm while (<STDIN>) { 2639d1e6fe8Sbluhm chomp; 264c64e9fedSbluhm print STDERR "<<< $_\n"; 2659d1e6fe8Sbluhm last if /^$/; 2669d1e6fe8Sbluhm if (/^Content-Length: (.*)/) { 26732fc6e3dSbluhm if ($self->{httpnok}) { 26832fc6e3dSbluhm $len = $1; 26932fc6e3dSbluhm } else { 2709d1e6fe8Sbluhm $1 == $len or die ref($self), 2719d1e6fe8Sbluhm " bad content length $1"; 2729d1e6fe8Sbluhm } 27332fc6e3dSbluhm } 2747582d965Sbluhm if (/^Transfer-Encoding: chunked$/) { 2757582d965Sbluhm $chunked = 1; 2769d1e6fe8Sbluhm } 2779d1e6fe8Sbluhm } 2787582d965Sbluhm } 279bfbf8851Sbenno if ($method ne 'HEAD') { 2807582d965Sbluhm if ($chunked) { 2817582d965Sbluhm read_chunked($self); 2827582d965Sbluhm } else { 283c6fb90fcSbluhm undef $len unless defined($vers) && $vers eq "1.1"; 284c6fb90fcSbluhm read_char($self, $len) 2853a4f532cSbluhm if $method eq "GET"; 2869d1e6fe8Sbluhm } 2877582d965Sbluhm } 288bfbf8851Sbenno} 2897582d965Sbluhm 2907582d965Sbluhmsub read_chunked { 2917582d965Sbluhm my $self = shift; 2927582d965Sbluhm 2937582d965Sbluhm for (;;) { 2947582d965Sbluhm my $len; 2957582d965Sbluhm { 2967582d965Sbluhm local $/ = "\r\n"; 2977582d965Sbluhm local $_ = <STDIN>; 2987582d965Sbluhm defined or die ref($self), " missing chunk size"; 2997582d965Sbluhm chomp; 300c64e9fedSbluhm print STDERR "<<< $_\n"; 3017582d965Sbluhm /^[[:xdigit:]]+$/ 3027582d965Sbluhm or die ref($self), " chunk size not hex: $_"; 3037582d965Sbluhm $len = hex; 3047582d965Sbluhm } 3057582d965Sbluhm last unless $len > 0; 3067582d965Sbluhm read_char($self, $len); 3077582d965Sbluhm { 3087582d965Sbluhm local $/ = "\r\n"; 3097582d965Sbluhm local $_ = <STDIN>; 3107582d965Sbluhm defined or die ref($self), " missing chunk data end"; 3117582d965Sbluhm chomp; 312c64e9fedSbluhm print STDERR "<<< $_\n"; 3137582d965Sbluhm /^$/ or die ref($self), " no chunk data end: $_"; 3147582d965Sbluhm } 3157582d965Sbluhm } 3167582d965Sbluhm { 3177582d965Sbluhm local $/ = "\r\n"; 3187582d965Sbluhm while (<STDIN>) { 3197582d965Sbluhm chomp; 320c64e9fedSbluhm print STDERR "<<< $_\n"; 3217582d965Sbluhm last if /^$/; 3227582d965Sbluhm } 3237582d965Sbluhm defined or die ref($self), " missing chunk trailer"; 3247582d965Sbluhm } 3257582d965Sbluhm} 326c2d4e910Sbluhm 327e1c6cc06Sbluhmsub errignore { 328e1c6cc06Sbluhm $SIG{PIPE} = 'IGNORE'; 329e1c6cc06Sbluhm $SIG{__DIE__} = sub { 330e1c6cc06Sbluhm die @_ if $^S; 331a7ce14b0Sbluhm warn "Error ignored"; 332e1c6cc06Sbluhm warn @_; 333e1c6cc06Sbluhm IO::Handle::flush(\*STDERR); 334e1c6cc06Sbluhm POSIX::_exit(0); 335e1c6cc06Sbluhm }; 336e1c6cc06Sbluhm} 337e1c6cc06Sbluhm 338c2d4e910Sbluhm######################################################################## 339c6fb90fcSbluhm# Common funcs 340c2d4e910Sbluhm######################################################################## 341c2d4e910Sbluhm 342c2d4e910Sbluhmsub read_char { 343c2d4e910Sbluhm my $self = shift; 3449d1e6fe8Sbluhm my $max = shift // $self->{max}; 345c2d4e910Sbluhm 3463daec375Sbenno if ($self->{fast}) { 347c6fb90fcSbluhm read_block($self, $max); 3483daec375Sbenno return; 3493daec375Sbenno } 3503daec375Sbenno 351c2d4e910Sbluhm my $ctx = Digest::MD5->new(); 352c2d4e910Sbluhm my $len = 0; 3539d1e6fe8Sbluhm if (defined($max) && $max == 0) { 3549d1e6fe8Sbluhm print STDERR "Max\n"; 3559d1e6fe8Sbluhm } else { 356c2d4e910Sbluhm while (<STDIN>) { 357c2d4e910Sbluhm $len += length($_); 358c2d4e910Sbluhm $ctx->add($_); 359c2d4e910Sbluhm print STDERR "."; 3609d1e6fe8Sbluhm if (defined($max) && $len >= $max) { 361c2d4e910Sbluhm print STDERR "\nMax"; 362c2d4e910Sbluhm last; 363c2d4e910Sbluhm } 364c2d4e910Sbluhm } 365c2d4e910Sbluhm print STDERR "\n"; 3669d1e6fe8Sbluhm } 367c2d4e910Sbluhm 368c2d4e910Sbluhm print STDERR "LEN: ", $len, "\n"; 369c2d4e910Sbluhm print STDERR "MD5: ", $ctx->hexdigest, "\n"; 370c2d4e910Sbluhm} 371c2d4e910Sbluhm 372c6fb90fcSbluhmsub read_block { 373c6fb90fcSbluhm my $self = shift; 374c6fb90fcSbluhm my $max = shift // $self->{max}; 375c6fb90fcSbluhm 376c6fb90fcSbluhm my $opct = 0; 377c6fb90fcSbluhm my $ctx = Digest::MD5->new(); 378c6fb90fcSbluhm my $len = 0; 379c6fb90fcSbluhm for (;;) { 380c6fb90fcSbluhm if (defined($max) && $len >= $max) { 381c6fb90fcSbluhm print STDERR "Max\n"; 382c6fb90fcSbluhm last; 383c6fb90fcSbluhm } 384c6fb90fcSbluhm my $rlen = POSIX::BUFSIZ; 385c6fb90fcSbluhm if (defined($max) && $rlen > $max - $len) { 386c6fb90fcSbluhm $rlen = $max - $len; 387c6fb90fcSbluhm } 388c6fb90fcSbluhm defined(my $n = read(STDIN, my $buf, $rlen)) 389c6fb90fcSbluhm or die ref($self), " read failed: $!"; 390c6fb90fcSbluhm $n or last; 391c6fb90fcSbluhm $len += $n; 392c6fb90fcSbluhm $ctx->add($buf); 393c6fb90fcSbluhm my $pct = ($len / $max) * 100.0; 394c6fb90fcSbluhm if ($pct >= $opct + 1) { 395c6fb90fcSbluhm printf(STDERR "%.2f%% $len/$max\n", $pct); 396c6fb90fcSbluhm $opct = $pct; 397c6fb90fcSbluhm } 398c6fb90fcSbluhm } 399c6fb90fcSbluhm 400c6fb90fcSbluhm print STDERR "LEN: ", $len, "\n"; 401c6fb90fcSbluhm print STDERR "MD5: ", $ctx->hexdigest, "\n"; 402c6fb90fcSbluhm} 403c6fb90fcSbluhm 404c6fb90fcSbluhm######################################################################## 405c6fb90fcSbluhm# Server funcs 406c6fb90fcSbluhm######################################################################## 407c6fb90fcSbluhm 4089d1e6fe8Sbluhmsub http_server { 4099d1e6fe8Sbluhm my $self = shift; 410e49fb922Sreyk my %header = %{$self->{header} || { Server => "Perl/".$^V }}; 41185781182Sandre my $cookie = $self->{cookie} || ""; 4129d1e6fe8Sbluhm 4133a4f532cSbluhm my($method, $url, $vers); 4149d1e6fe8Sbluhm do { 4153a4f532cSbluhm my $len; 4169d1e6fe8Sbluhm { 4179d1e6fe8Sbluhm local $/ = "\r\n"; 4189d1e6fe8Sbluhm local $_ = <STDIN>; 4199d1e6fe8Sbluhm return unless defined $_; 4209d1e6fe8Sbluhm chomp; 421c64e9fedSbluhm print STDERR "<<< $_\n"; 4223a4f532cSbluhm ($method, $url, $vers) = m{^(\w+) (.*) HTTP/(1\.[01])$} 4239d1e6fe8Sbluhm or die ref($self), " http request not ok"; 424bfbf8851Sbenno $method =~ /^(GET|HEAD|PUT)$/ 4253a4f532cSbluhm or die ref($self), " unknown method: $method"; 4267582d965Sbluhm ($len, my @chunks) = $url =~ /(\d+)/g; 4277582d965Sbluhm $len = [ $len, @chunks ] if @chunks; 4289d1e6fe8Sbluhm while (<STDIN>) { 4299d1e6fe8Sbluhm chomp; 430c64e9fedSbluhm print STDERR "<<< $_\n"; 4319d1e6fe8Sbluhm last if /^$/; 432a3c410c1Sbluhm if ($method eq "PUT" && 433a3c410c1Sbluhm /^Content-Length: (.*)/) { 4343a4f532cSbluhm $1 == $len or die ref($self), 4353a4f532cSbluhm " bad content length $1"; 4369d1e6fe8Sbluhm } 437cab60319Sbluhm $cookie ||= $1 if /^Cookie: (.*)/; 4389d1e6fe8Sbluhm } 4393a4f532cSbluhm } 4401b3c3ba0Sbluhm if ($method eq "PUT" ) { 4411b3c3ba0Sbluhm if (ref($len) eq 'ARRAY') { 4421b3c3ba0Sbluhm read_chunked($self); 4431b3c3ba0Sbluhm } else { 4441b3c3ba0Sbluhm read_char($self, $len); 4451b3c3ba0Sbluhm } 4461b3c3ba0Sbluhm } 4479d1e6fe8Sbluhm 448a3c410c1Sbluhm my @response = ("HTTP/$vers 200 OK"); 44985781182Sandre $len = defined($len) ? $len : scalar(split /|/,$url); 450bfbf8851Sbenno if ($vers eq "1.1" && $method =~ /^(GET|HEAD)$/) { 4517582d965Sbluhm if (ref($len) eq 'ARRAY') { 4521b3c3ba0Sbluhm push @response, "Transfer-Encoding: chunked"; 4537582d965Sbluhm } else { 4541b3c3ba0Sbluhm push @response, "Content-Length: $len"; 4551b3c3ba0Sbluhm } 4567582d965Sbluhm } 4577e1bbd8eSreyk foreach my $key (sort keys %header) { 4587e1bbd8eSreyk my $val = $header{$key}; 4597e1bbd8eSreyk if (ref($val) eq 'ARRAY') { 4607e1bbd8eSreyk push @response, "$key: $_" 4617e1bbd8eSreyk foreach @{$val}; 4627e1bbd8eSreyk } else { 4637e1bbd8eSreyk push @response, "$key: $val"; 4647e1bbd8eSreyk } 4657e1bbd8eSreyk } 466cab60319Sbluhm push @response, "Set-Cookie: $cookie" if $cookie; 467a3c410c1Sbluhm push @response, ""; 46885781182Sandre 469a3c410c1Sbluhm print STDERR map { ">>> $_\n" } @response; 470a3c410c1Sbluhm print map { "$_\r\n" } @response; 471c64e9fedSbluhm 4721b3c3ba0Sbluhm if ($method eq "GET") { 4737582d965Sbluhm if (ref($len) eq 'ARRAY') { 474cab60319Sbluhm if ($vers eq "1.1") { 4757582d965Sbluhm write_chunked($self, @$len); 4767582d965Sbluhm } else { 477cab60319Sbluhm write_char($self, $_) foreach (@$len); 478cab60319Sbluhm } 479cab60319Sbluhm } else { 4801b3c3ba0Sbluhm write_char($self, $len); 4811b3c3ba0Sbluhm } 4827582d965Sbluhm } 4839d1e6fe8Sbluhm IO::Handle::flush(\*STDOUT); 4849d1e6fe8Sbluhm } while ($vers eq "1.1"); 485cab60319Sbluhm $self->{redo}-- if $self->{redo}; 4869d1e6fe8Sbluhm} 4879d1e6fe8Sbluhm 4887582d965Sbluhmsub write_chunked { 4897582d965Sbluhm my $self = shift; 4907582d965Sbluhm my @chunks = @_; 4917582d965Sbluhm 4927582d965Sbluhm foreach my $len (@chunks) { 493c64e9fedSbluhm printf STDERR ">>> %x\n", $len; 4947582d965Sbluhm printf "%x\r\n", $len; 4957582d965Sbluhm write_char($self, $len); 496c64e9fedSbluhm printf STDERR ">>> \n"; 4977582d965Sbluhm print "\r\n"; 4987582d965Sbluhm } 499c64e9fedSbluhm my @trailer = ("0", "X-Chunk-Trailer: @chunks", ""); 500c64e9fedSbluhm print STDERR map { ">>> $_\n" } @trailer; 501c64e9fedSbluhm print map { "$_\r\n" } @trailer; 5027582d965Sbluhm} 5037582d965Sbluhm 504a7ce14b0Sbluhm######################################################################## 505a7ce14b0Sbluhm# Script funcs 506a7ce14b0Sbluhm######################################################################## 507a7ce14b0Sbluhm 508a7ce14b0Sbluhmsub check_logs { 509a7ce14b0Sbluhm my ($c, $r, $s, %args) = @_; 510a7ce14b0Sbluhm 511a7ce14b0Sbluhm return if $args{nocheck}; 512a7ce14b0Sbluhm 513a7ce14b0Sbluhm check_len($c, $r, $s, %args); 514a7ce14b0Sbluhm check_md5($c, $r, $s, %args); 515a7ce14b0Sbluhm check_loggrep($c, $r, $s, %args); 5161b3c3ba0Sbluhm $r->loggrep("lost child") 5171b3c3ba0Sbluhm and die "relayd lost child"; 518a7ce14b0Sbluhm} 519a7ce14b0Sbluhm 520*508def2aSbluhmsub array_eq { 521*508def2aSbluhm my ($a, $b) = @_; 522*508def2aSbluhm return if @$a != @$b; 523*508def2aSbluhm for (my $i = 0; $i < @$a; $i++) { 524*508def2aSbluhm return if $$a[$i] ne $$b[$i]; 525*508def2aSbluhm } 526*508def2aSbluhm return 1; 527*508def2aSbluhm} 528*508def2aSbluhm 529a7ce14b0Sbluhmsub check_len { 530a7ce14b0Sbluhm my ($c, $r, $s, %args) = @_; 531a7ce14b0Sbluhm 5326d509cd0Sbluhm $args{len} ||= 251 unless $args{lengths}; 5336d509cd0Sbluhm 534f8f8530cSbluhm my (@clen, @slen); 535f8f8530cSbluhm @clen = $c->loggrep(qr/^LEN: /) or die "no client len" 536a7ce14b0Sbluhm unless $args{client}{nocheck}; 537f8f8530cSbluhm @slen = $s->loggrep(qr/^LEN: /) or die "no server len" 538a7ce14b0Sbluhm unless $args{server}{nocheck}; 539*508def2aSbluhm !@clen || !@slen || array_eq \@clen, \@slen 540a7ce14b0Sbluhm or die "client: @clen", "server: @slen", "len mismatch"; 541a7ce14b0Sbluhm !defined($args{len}) || !$clen[0] || $clen[0] eq "LEN: $args{len}\n" 542a7ce14b0Sbluhm or die "client: $clen[0]", "len $args{len} expected"; 543a7ce14b0Sbluhm !defined($args{len}) || !$slen[0] || $slen[0] eq "LEN: $args{len}\n" 544a7ce14b0Sbluhm or die "server: $slen[0]", "len $args{len} expected"; 545a7ce14b0Sbluhm my @lengths = map { ref eq 'ARRAY' ? @$_ : $_ } 546a7ce14b0Sbluhm @{$args{lengths} || []}; 547a7ce14b0Sbluhm foreach my $len (@lengths) { 548a3c410c1Sbluhm unless ($args{client}{nocheck}) { 549a7ce14b0Sbluhm my $clen = shift @clen; 550a7ce14b0Sbluhm $clen eq "LEN: $len\n" 551a7ce14b0Sbluhm or die "client: $clen", "len $len expected"; 552a3c410c1Sbluhm } 553a3c410c1Sbluhm unless ($args{server}{nocheck}) { 554a7ce14b0Sbluhm my $slen = shift @slen; 555a7ce14b0Sbluhm $slen eq "LEN: $len\n" 556a7ce14b0Sbluhm or die "server: $slen", "len $len expected"; 557a7ce14b0Sbluhm } 558a7ce14b0Sbluhm } 559a3c410c1Sbluhm} 560a7ce14b0Sbluhm 561a7ce14b0Sbluhmsub check_md5 { 562a7ce14b0Sbluhm my ($c, $r, $s, %args) = @_; 563a7ce14b0Sbluhm 564f8f8530cSbluhm my (@cmd5, @smd5); 565f8f8530cSbluhm @cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck}; 566f8f8530cSbluhm @smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck}; 5674812e4f6Sbluhm !@cmd5 || !@smd5 || $cmd5[0] eq $smd5[0] 5684812e4f6Sbluhm or die "client: $cmd5[0]", "server: $smd5[0]", "md5 mismatch"; 5694812e4f6Sbluhm 5704812e4f6Sbluhm my @md5 = ref($args{md5}) eq 'ARRAY' ? @{$args{md5}} : $args{md5} || () 5714812e4f6Sbluhm or return; 5724812e4f6Sbluhm foreach my $md5 (@md5) { 5734812e4f6Sbluhm unless ($args{client}{nocheck}) { 5744812e4f6Sbluhm my $cmd5 = shift @cmd5 5754812e4f6Sbluhm or die "too few md5 in client log"; 5764812e4f6Sbluhm $cmd5 =~ /^MD5: ($md5)$/ 577a7ce14b0Sbluhm or die "client: $cmd5", "md5 $md5 expected"; 5784812e4f6Sbluhm } 5794812e4f6Sbluhm unless ($args{server}{nocheck}) { 5804812e4f6Sbluhm my $smd5 = shift @smd5 5814812e4f6Sbluhm or die "too few md5 in server log"; 5824812e4f6Sbluhm $smd5 =~ /^MD5: ($md5)$/ 583a7ce14b0Sbluhm or die "server: $smd5", "md5 $md5 expected"; 584a7ce14b0Sbluhm } 5854812e4f6Sbluhm } 5864812e4f6Sbluhm @cmd5 && ref($args{md5}) eq 'ARRAY' 5874812e4f6Sbluhm and die "too many md5 in client log"; 5884812e4f6Sbluhm @smd5 && ref($args{md5}) eq 'ARRAY' 5894812e4f6Sbluhm and die "too many md5 in server log"; 5904812e4f6Sbluhm} 591a7ce14b0Sbluhm 592a7ce14b0Sbluhmsub check_loggrep { 593a7ce14b0Sbluhm my ($c, $r, $s, %args) = @_; 594a7ce14b0Sbluhm 595a7ce14b0Sbluhm my %name2proc = (client => $c, relayd => $r, server => $s); 596a7ce14b0Sbluhm foreach my $name (qw(client relayd server)) { 597aa8f1300Sbluhm my $p = $name2proc{$name} or next; 598a7ce14b0Sbluhm my $pattern = $args{$name}{loggrep} or next; 599a7ce14b0Sbluhm $pattern = [ $pattern ] unless ref($pattern) eq 'ARRAY'; 600a7ce14b0Sbluhm foreach my $pat (@$pattern) { 601a7ce14b0Sbluhm if (ref($pat) eq 'HASH') { 602a7ce14b0Sbluhm while (my($re, $num) = each %$pat) { 603a7ce14b0Sbluhm my @matches = $p->loggrep($re); 604a7ce14b0Sbluhm @matches == $num 605aa8f1300Sbluhm or die "$name matches '@matches': ", 606aa8f1300Sbluhm "'$re' => $num"; 607a7ce14b0Sbluhm } 608a7ce14b0Sbluhm } else { 609a7ce14b0Sbluhm $p->loggrep($pat) 610aa8f1300Sbluhm or die "$name log missing pattern: '$pat'"; 611a7ce14b0Sbluhm } 612a7ce14b0Sbluhm } 613a7ce14b0Sbluhm } 614a7ce14b0Sbluhm} 615a7ce14b0Sbluhm 616c2d4e910Sbluhm1; 617