HTTP.pm revision 1.2
1#! /usr/bin/perl 2# ex:ts=8 sw=4: 3# $OpenBSD: HTTP.pm,v 1.2 2011/07/12 10:29:20 espie Exp $ 4# 5# Copyright (c) 2011 Marc Espie <espie@openbsd.org> 6# 7# Permission to use, copy, modify, and distribute this software for any 8# purpose with or without fee is hereby granted, provided that the above 9# copyright notice and this permission notice appear in all copies. 10# 11# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 19use strict; 20use warnings; 21 22package OpenBSD::Repository::HTTP; 23sub urlscheme 24{ 25 return 'http'; 26} 27 28sub initiate 29{ 30 my $self = shift; 31 my ($rdfh, $wrfh); 32 pipe($self->{getfh}, $h1); 33 pipe($h2, $self->{cmdfh}); 34 my $pid = fork(); 35 if ($pid == 0) { 36 close($self->{getfh}); 37 close($self->{cmdfh}); 38 close(STDOUT); 39 close(STDIN); 40 open(STDOUT, '>&', $wrfh); 41 open(STDIN, '<&', $rdfh); 42 _Proxy::main($self); 43 } else { 44 close($rdfh); 45 close($wrfh); 46 $self->{controller} = $pid; 47 } 48} 49 50package _Proxy::connection; 51sub new 52{ 53 my ($class, $host, $port) = @_; 54 require IO::Socket::INET; 55 my $o = IO::Socket::INET->new( 56 PeerHost => $host, 57 PeerPort => $port); 58 bless {fh => $o, buffer => ''}, $class; 59} 60 61sub getline 62{ 63 my $self = shift; 64 while (1) { 65 if ($self->{buffer} =~ s/^(.*?)\015\012//) { 66 return $1; 67 } 68 my $buffer; 69 $self->{fh}->recv($buffer, 1024); 70 $self->{buffer}.=$buffer; 71 } 72} 73 74sub retrieve 75{ 76 my ($self, $sz) = @_; 77 while(length($self->{buffer}) < $sz) { 78 my $buffer; 79 $self->{fh}->recv($buffer, $sz - length($self->{buffer})); 80 $self->{buffer}.=$buffer; 81 } 82 my $result= substr($self->{buffer}, 0, $sz); 83 $self->{buffer} = substr($self->{buffer}, $sz); 84 return $result; 85} 86 87sub print 88{ 89 my ($self, @l) = @_; 90 print {$self->{fh}} @l; 91} 92 93package _Proxy; 94 95my $pid; 96my $token = 0; 97 98sub batch(&) 99{ 100 my $code = shift; 101 if (defined $pid) { 102 waitpid($pid, 0); 103 undef $pid; 104 } 105 $token++; 106 $pid = fork(); 107 if (!defined $pid) { 108 print "ERROR: fork failed: $!\n"; 109 } 110 if ($pid == 0) { 111 &$code(); 112 exit(0); 113 } 114} 115 116sub abort_batch() 117{ 118 if (defined $pid) { 119 kill 1, $pid; 120 waitpid($pid, 0); 121 undef $pid; 122 } 123 print "\nABORTED $token\n"; 124} 125 126sub main 127{ 128 my $self = shift; 129 my $o = _Proxy::Connection->new($self->{host}, "www"); 130 while (<STDIN>) { 131 chomp; 132 if (m/^LIST\s+(.*)$/o) { 133 my $dname = $1; 134 batch(sub { 135 my $d; 136 if (opendir($d, $dname)) { 137 print "SUCCESS: directory $dname\n"; 138 } else { 139 print "ERROR: bad directory $dname $!\n"; 140 } 141 while (my $e = readdir($d)) { 142 next if $e eq '.' or $e eq '..'; 143 next unless $e =~ m/(.+)\.tgz$/; 144 next unless -f "$dname/$e"; 145 print "$1\n"; 146 } 147 print "\n"; 148 closedir($d); 149 }); 150 } elsif (m/^GET\s+(.*)$/o) { 151 my $fname = $1; 152 batch(sub { 153 if (open(my $fh, '<', $fname)) { 154 my $size = (stat $fh)[7]; 155 print "TRANSFER: $size\n"; 156 my $buffer = ''; 157 while (read($fh, $buffer, 1024 * 1024) > 0) { 158 print $buffer; 159 } 160 close($fh); 161 } else { 162 print "ERROR: bad file $fname $!\n"; 163 } 164 }); 165 } elsif (m/^BYE$/o) { 166 exit(0); 167 } elsif (m/^ABORT$/o) { 168 abort_batch(); 169 } else { 170 print "ERROR: Unknown command\n"; 171 } 172 } 173} 174 175 176sub get_file 177{ 178 my ($o, $file) = @_; 179 my $crlf="\015\012"; 180 open my $fh, '>', $file; 181 182 my $start = 0; 183 my $end = 4000; 184 my $total_size = 0; 185 186 do { 187 $end *= 2; 188 $o->print("GET /pub/OpenBSD/snapshots/packages/amd64/$file HTTP/1.1$crlf", 189 "Host: www.w3.org$crlf", 190 "Range: bytes=",$start, "-", $end-1, $crlf, $crlf); 191 192 # get header 193 194 my $_ = $o->getline; 195 if (m,^HTTP/1\.1\s+(\d\d\d),) { 196 my $code = $1; 197 print "Code: $code\n"; 198 } else { 199 print $_, "\n"; 200 } 201 my $h = {}; 202 while ($_ = $o->getline) { 203 last if m/^$/; 204 if (m/^([\w\-]+)\:\s*(.*)$/) { 205 print "$1 => $2\n"; 206 $h->{$1} = $2; 207 } else { 208 print "unknown line: $_\n"; 209 } 210 } 211 212 if (defined $h->{'Content-Range'} && $h->{'Content-Range'} =~ 213 m/^bytes\s+\d+\-\d+\/(\d+)/) { 214 $total_size = $1; 215 } 216 print "END OF HEADER\n"; 217 218 if (defined $h->{'Content-Length'}) { 219 my $v = $o->retrieve($h->{'Content-Length'}); 220 print $fh $v; 221 } 222 $start = $end; 223 } while ($end < $total_size); 224} 225