HTTP.pm revision 1.3
1#! /usr/bin/perl 2# ex:ts=8 sw=4: 3# $OpenBSD: HTTP.pm,v 1.3 2011/07/18 19:42:32 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}, $rdfh); 33 pipe($wrfh, $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, host => $host, 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 get_directory 127{ 128 my ($o, $dname) = @_; 129 my $crlf="\015\012"; 130 $o->print("GET $dname/ HTTP/1.1", $crlf, 131 "Host: ", $o->{host}, $crlf, $crlf); 132 # get header 133 134 my $_ = $o->getline; 135 if (!m,^HTTP/1\.1\s+(\d\d\d),) { 136 print "ABORTED\n"; 137 return; 138 } 139 my $code = $1; 140 if ($code != 200) { 141 print "ABORTED"; 142 return; 143 } 144 my $h = {}; 145 while ($_ = $o->getline) { 146 last if m/^$/; 147 if (m/^([\w\-]+)\:\s*(.*)$/) { 148 print "$1 => $2\n"; 149 $h->{$1} = $2; 150 } else { 151 print "unknown line: $_\n"; 152 } 153 } 154} 155 156sub main 157{ 158 my $self = shift; 159 my $o = _Proxy::Connection->new($self->{host}, "www"); 160 while (<STDIN>) { 161 chomp; 162 if (m/^LIST\s+(.*)$/o) { 163 my $dname = $1; 164 batch(sub {get_directory($o, $dname);}); 165 } elsif (m/^GET\s+(.*)$/o) { 166 my $fname = $1; 167 batch(sub { 168 if (open(my $fh, '<', $fname)) { 169 my $size = (stat $fh)[7]; 170 print "TRANSFER: $size\n"; 171 my $buffer = ''; 172 while (read($fh, $buffer, 1024 * 1024) > 0) { 173 print $buffer; 174 } 175 close($fh); 176 } else { 177 print "ERROR: bad file $fname $!\n"; 178 } 179 }); 180 } elsif (m/^BYE$/o) { 181 exit(0); 182 } elsif (m/^ABORT$/o) { 183 abort_batch(); 184 } else { 185 print "ERROR: Unknown command\n"; 186 } 187 } 188} 189 190 191sub get_file 192{ 193 my ($o, $file) = @_; 194 my $crlf="\015\012"; 195 open my $fh, '>', $file; 196 197 my $start = 0; 198 my $end = 4000; 199 my $total_size = 0; 200 201 do { 202 $end *= 2; 203 $o->print("GET /pub/OpenBSD/snapshots/packages/amd64/$file HTTP/1.1$crlf", 204 "Host: www.w3.org$crlf", 205 "Range: bytes=",$start, "-", $end-1, $crlf, $crlf); 206 207 # get header 208 209 my $_ = $o->getline; 210 if (m,^HTTP/1\.1\s+(\d\d\d),) { 211 my $code = $1; 212 print "Code: $code\n"; 213 } else { 214 print $_, "\n"; 215 } 216 my $h = {}; 217 while ($_ = $o->getline) { 218 last if m/^$/; 219 if (m/^([\w\-]+)\:\s*(.*)$/) { 220 print "$1 => $2\n"; 221 $h->{$1} = $2; 222 } else { 223 print "unknown line: $_\n"; 224 } 225 } 226 227 if (defined $h->{'Content-Range'} && $h->{'Content-Range'} =~ 228 m/^bytes\s+\d+\-\d+\/(\d+)/) { 229 $total_size = $1; 230 } 231 print "END OF HEADER\n"; 232 233 if (defined $h->{'Content-Length'}) { 234 my $v = $o->retrieve($h->{'Content-Length'}); 235 print $fh $v; 236 } 237 $start = $end; 238 } while ($end < $total_size); 239} 240 2411; 242